1 | #if defined(OS_LINUX) |
2 | #include <malloc.h> |
3 | #elif defined(OS_DARWIN) |
4 | #include <malloc/malloc.h> |
5 | #endif |
6 | #include <new> |
7 | |
8 | #include <common/config_common.h> |
9 | #include <common/memory.h> |
10 | #include <Common/MemoryTracker.h> |
11 | |
12 | /// Replace default new/delete with memory tracking versions. |
13 | /// @sa https://en.cppreference.com/w/cpp/memory/new/operator_new |
14 | /// https://en.cppreference.com/w/cpp/memory/new/operator_delete |
15 | #if !UNBUNDLED |
16 | |
17 | namespace Memory |
18 | { |
19 | |
20 | ALWAYS_INLINE void trackMemory(std::size_t size) |
21 | { |
22 | #if USE_JEMALLOC |
23 | /// The nallocx() function allocates no memory, but it performs the same size computation as the mallocx() function |
24 | /// @note je_mallocx() != je_malloc(). It's expected they don't differ much in allocation logic. |
25 | if (likely(size != 0)) |
26 | CurrentMemoryTracker::alloc(nallocx(size, 0)); |
27 | #else |
28 | CurrentMemoryTracker::alloc(size); |
29 | #endif |
30 | } |
31 | |
32 | ALWAYS_INLINE bool trackMemoryNoExept(std::size_t size) noexcept |
33 | { |
34 | try |
35 | { |
36 | trackMemory(size); |
37 | } |
38 | catch (...) |
39 | { |
40 | return false; |
41 | } |
42 | |
43 | return true; |
44 | } |
45 | |
46 | ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]] = 0) noexcept |
47 | { |
48 | try |
49 | { |
50 | #if USE_JEMALLOC |
51 | /// @note It's also possible to use je_malloc_usable_size() here. |
52 | if (likely(ptr != nullptr)) |
53 | CurrentMemoryTracker::free(sallocx(ptr, 0)); |
54 | #else |
55 | if (size) |
56 | CurrentMemoryTracker::free(size); |
57 | #ifdef _GNU_SOURCE |
58 | /// It's innaccurate resource free for sanitizers. malloc_usable_size() result is greater or equal to allocated size. |
59 | else |
60 | CurrentMemoryTracker::free(malloc_usable_size(ptr)); |
61 | #endif |
62 | #endif |
63 | } |
64 | catch (...) |
65 | {} |
66 | } |
67 | |
68 | } |
69 | |
70 | /// new |
71 | |
72 | void * operator new(std::size_t size) |
73 | { |
74 | Memory::trackMemory(size); |
75 | return Memory::newImpl(size); |
76 | } |
77 | |
78 | void * operator new[](std::size_t size) |
79 | { |
80 | Memory::trackMemory(size); |
81 | return Memory::newImpl(size); |
82 | } |
83 | |
84 | void * operator new(std::size_t size, const std::nothrow_t &) noexcept |
85 | { |
86 | if (likely(Memory::trackMemoryNoExept(size))) |
87 | return Memory::newNoExept(size); |
88 | return nullptr; |
89 | } |
90 | |
91 | void * operator new[](std::size_t size, const std::nothrow_t &) noexcept |
92 | { |
93 | if (likely(Memory::trackMemoryNoExept(size))) |
94 | return Memory::newNoExept(size); |
95 | return nullptr; |
96 | } |
97 | |
98 | /// delete |
99 | |
100 | /// C++17 std 21.6.2.1 (11) |
101 | /// If a function without a size parameter is defined, the program should also define the corresponding function with a size parameter. |
102 | /// If a function with a size parameter is defined, the program shall also define the corresponding version without the size parameter. |
103 | |
104 | /// cppreference: |
105 | /// It's unspecified whether size-aware or size-unaware version is called when deleting objects of |
106 | /// incomplete type and arrays of non-class and trivially-destructible class types. |
107 | |
108 | void operator delete(void * ptr) noexcept |
109 | { |
110 | Memory::untrackMemory(ptr); |
111 | Memory::deleteImpl(ptr); |
112 | } |
113 | |
114 | void operator delete[](void * ptr) noexcept |
115 | { |
116 | Memory::untrackMemory(ptr); |
117 | Memory::deleteImpl(ptr); |
118 | } |
119 | |
120 | void operator delete(void * ptr, std::size_t size) noexcept |
121 | { |
122 | Memory::untrackMemory(ptr, size); |
123 | Memory::deleteSized(ptr, size); |
124 | } |
125 | |
126 | void operator delete[](void * ptr, std::size_t size) noexcept |
127 | { |
128 | Memory::untrackMemory(ptr, size); |
129 | Memory::deleteSized(ptr, size); |
130 | } |
131 | |
132 | #else |
133 | |
134 | /// new |
135 | |
136 | void * operator new(std::size_t size) { return Memory::newImpl(size); } |
137 | void * operator new[](std::size_t size) { return Memory::newImpl(size); } |
138 | |
139 | void * operator new(std::size_t size, const std::nothrow_t &) noexcept { return Memory::newNoExept(size); } |
140 | void * operator new[](std::size_t size, const std::nothrow_t &) noexcept { return Memory::newNoExept(size); } |
141 | |
142 | /// delete |
143 | |
144 | void operator delete(void * ptr) noexcept { Memory::deleteImpl(ptr); } |
145 | void operator delete[](void * ptr) noexcept { Memory::deleteImpl(ptr); } |
146 | |
147 | void operator delete(void * ptr, const std::nothrow_t &) noexcept { Memory::deleteImpl(ptr); } |
148 | void operator delete[](void * ptr, const std::nothrow_t &) noexcept { Memory::deleteImpl(ptr); } |
149 | |
150 | void operator delete(void * ptr, std::size_t size) noexcept { Memory::deleteSized(ptr, size); } |
151 | void operator delete[](void * ptr, std::size_t size) noexcept { Memory::deleteSized(ptr, size); } |
152 | |
153 | #endif |
154 | |