1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18/* Global replacement of operators new and delete.
19 *
20 * By linking this file into a binary we globally replace new & delete.
21 *
22 * This allows us to track how much memory has been allocated from C++ code,
23 * both in total and per ep-engine instance, by allowing interested parties to
24 * register hook functions for new and delete.
25 *
26 * Usage:
27 *
28 * Link this file into /all/ binaries (executables, shared libraries and DLLs)
29 * for which you want to have memory tracking for C++ allocations.
30 *
31 * (Note: On most *ix-like platforms (Linux and OS X tested), it is sufficient
32 *        to link this file into the main executable (e.g. `memcached`) and
33 *        all shared libraries and dlopen'ed plugins will use these functions
34 *        automagically. However this isn't the case on Windows/MSVC, where you
35 *        need to link into all binaries for the overrider to take effect.
36 *
37 * Implementation:
38 *
39 * From C++11 onwards overriding base `operator new` and `operator delete`
40 * /should/ be sufficient to all C++ allocations, however this isn't true on
41 * Windows/MSVC, where we also need to override the array forms.
42 * http://en.cppreference.com/w/cpp/memory/new/operator_new#Global_replacements
43 *
44 * However, since our allocator of choice is jemalloc, who decided to overload
45 * the new[] and delete[] operators since version 5.1.0 as part of sized
46 * deallocation, we need to overload the array form operators on all platforms.
47 * https://github.com/jemalloc/jemalloc/commit/2319152d9f5d9b33eebc36a50ccf4239f31c1ad9
48 */
49
50#include <platform/cb_malloc.h>
51
52#include <cstdlib>
53#include <new>
54
55#if defined(WIN32)
56#  define NOEXCEPT
57#else
58#  define NOEXCEPT noexcept
59
60// Silence GCCs warning about redundant redeclaration (again, this will go
61// away soon).
62#pragma GCC diagnostic push
63#pragma GCC diagnostic ignored "-Wredundant-decls"
64void* operator new(std::size_t count);
65void operator delete(void* ptr) NOEXCEPT;
66#pragma GCC diagnostic pop
67
68#endif
69
70void* operator new(std::size_t count) {
71    void* result = cb_malloc(count);
72    if (result == nullptr) {
73        throw std::bad_alloc();
74    }
75    return result;
76}
77
78void* operator new(std::size_t count, const std::nothrow_t& tag) NOEXCEPT {
79    void* result = cb_malloc(count);
80    return result;
81}
82
83void operator delete(void* ptr) NOEXCEPT {
84    cb_free(ptr);
85}
86
87void operator delete(void* ptr, const std::nothrow_t& tag)NOEXCEPT {
88    cb_free(ptr);
89}
90
91void operator delete(void* ptr, std::size_t size) NOEXCEPT {
92    cb_sized_free(ptr, size);
93}
94
95void* operator new[](std::size_t count) {
96    void* result = cb_malloc(count);
97    if (result == nullptr) {
98        throw std::bad_alloc();
99    }
100    return result;
101}
102
103void* operator new[](std::size_t count, const std::nothrow_t& tag) NOEXCEPT {
104    void* result = cb_malloc(count);
105    return result;
106}
107
108void operator delete[](void *ptr) NOEXCEPT {
109    cb_free(ptr);
110}
111
112void operator delete[](void *ptr, std::size_t size) NOEXCEPT {
113    cb_sized_free(ptr, size);
114}
115
116void operator delete[](void* ptr, const std::nothrow_t& tag) NOEXCEPT {
117    cb_free(ptr);
118}
119
120/* As we have a global new replacement, libraries could end up calling the
121 * system malloc_usable_size (if present) with a pointer to memory
122 * allocated by different allocator. This interposes malloc_usable_size
123 * to ensure the malloc_usable_size of the desired allocator is called.
124 *
125 * In the case where our desired allocator is the system allocator, there exists
126 * an issue in the cb_malloc_usable_size implementation where, because the
127 * malloc_usable_size call is not prefixed, we get stuck in an infinite
128 * recursive loop causing a stack overflow as our attempt to forward on the
129 * malloc_usable_size call brings us back to this overload. This can be fixed
130 * by only defining this overload if we are not using the system allocator.
131 * This was spotted under TSan where we use the system allocator. For some
132 * unknown reason, when we define the sized delete operator overload the runtime
133 * linking order changes. We now link the new, delete, and malloc_usable_size
134 * symbols in this file to a test suite before we link the operators in RocksDB.
135 * This causes RocksDB to link to the symbols in this file, whereas previously
136 * it would link directly to TSan's overloaded operators.
137 */
138#if defined(HAVE_MALLOC_USABLE_SIZE) and !defined(HAVE_SYSTEM_MALLOC)
139extern "C" PLATFORM_PUBLIC_API size_t malloc_usable_size(void* ptr) {
140    return cb_malloc_usable_size(ptr);
141}
142#endif
143