1 /** @file
2
3 Memory allocation routines for libts
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23 #include "tscore/ink_platform.h"
24 #include "tscore/ink_memory.h"
25 #include "tscore/ink_defs.h"
26 #include "tscore/ink_stack_trace.h"
27 #include "tscore/Diags.h"
28 #include "tscore/ink_atomic.h"
29
30 #if !defined(kfreebsd) && defined(freebsd) && !defined(__DragonFly__)
31 #include <malloc_np.h> // for malloc_usable_size
32 #endif
33
34 #include <cassert>
35 #if defined(linux)
36 // XXX: Shouldn't that be part of CPPFLAGS?
37 #ifndef _XOPEN_SOURCE
38 #define _XOPEN_SOURCE 600
39 #endif
40 #endif
41 #include <cstdlib>
42 #include <cstring>
43
44 void *
ats_malloc(size_t size)45 ats_malloc(size_t size)
46 {
47 void *ptr = nullptr;
48
49 /*
50 * There's some nasty code in libts that expects
51 * a MALLOC of a zero-sized item to work properly. Rather
52 * than allocate any space, we simply return a nullptr to make
53 * certain they die quickly & don't trash things.
54 */
55
56 // Useful for tracing bad mallocs
57 // ink_stack_trace_dump();
58 if (likely(size > 0)) {
59 if (unlikely((ptr = malloc(size)) == nullptr)) {
60 ink_abort("couldn't allocate %zu bytes", size);
61 }
62 }
63 return ptr;
64 } /* End ats_malloc */
65
66 void *
ats_calloc(size_t nelem,size_t elsize)67 ats_calloc(size_t nelem, size_t elsize)
68 {
69 void *ptr = calloc(nelem, elsize);
70 if (unlikely(ptr == nullptr)) {
71 ink_abort("couldn't allocate %zu %zu byte elements", nelem, elsize);
72 }
73 return ptr;
74 } /* End ats_calloc */
75
76 void *
ats_realloc(void * ptr,size_t size)77 ats_realloc(void *ptr, size_t size)
78 {
79 void *newptr = realloc(ptr, size);
80 if (unlikely(newptr == nullptr)) {
81 ink_abort("couldn't reallocate %zu bytes", size);
82 }
83 return newptr;
84 } /* End ats_realloc */
85
86 // TODO: For Win32 platforms, we need to figure out what to do with memalign.
87 // The older code had ifdef's around such calls, turning them into ats_malloc().
88 void *
ats_memalign(size_t alignment,size_t size)89 ats_memalign(size_t alignment, size_t size)
90 {
91 void *ptr;
92
93 if (alignment <= 8) {
94 return ats_malloc(size);
95 }
96
97 #if defined(openbsd)
98 if (alignment > PAGE_SIZE)
99 alignment = PAGE_SIZE;
100 #endif
101
102 int retcode = posix_memalign(&ptr, alignment, size);
103
104 if (unlikely(retcode)) {
105 if (retcode == EINVAL) {
106 ink_abort("couldn't allocate %zu bytes at alignment %zu - invalid alignment parameter", size, alignment);
107 } else if (retcode == ENOMEM) {
108 ink_abort("couldn't allocate %zu bytes at alignment %zu - insufficient memory", size, alignment);
109 } else {
110 ink_abort("couldn't allocate %zu bytes at alignment %zu - unknown error %d", size, alignment, retcode);
111 }
112 }
113
114 return ptr;
115 } /* End ats_memalign */
116
117 void
ats_free(void * ptr)118 ats_free(void *ptr)
119 {
120 if (likely(ptr != nullptr)) {
121 free(ptr);
122 }
123 } /* End ats_free */
124
125 void *
ats_free_null(void * ptr)126 ats_free_null(void *ptr)
127 {
128 if (likely(ptr != nullptr)) {
129 free(ptr);
130 }
131 return nullptr;
132 } /* End ats_free_null */
133
134 // This effectively makes mallopt() a no-op (currently) when tcmalloc
135 // or jemalloc is used. This might break our usage for increasing the
136 // number of mmap areas (ToDo: Do we still really need that??).
137 //
138 // TODO: I think we might be able to get rid of this?
139 int
ats_mallopt(int param ATS_UNUSED,int value ATS_UNUSED)140 ats_mallopt(int param ATS_UNUSED, int value ATS_UNUSED)
141 {
142 #if TS_HAS_JEMALLOC
143 // TODO: jemalloc code ?
144 #else
145 #if TS_HAS_TCMALLOC
146 // TODO: tcmalloc code ?
147 #else
148 #if defined(__GLIBC__)
149 return mallopt(param, value);
150 #endif // ! defined(__GLIBC__)
151 #endif // ! TS_HAS_TCMALLOC
152 #endif // ! TS_HAS_JEMALLOC
153 return 0;
154 }
155
156 ats_unique_buf
ats_unique_malloc(size_t size)157 ats_unique_malloc(size_t size)
158 {
159 return ats_unique_buf(static_cast<uint8_t *>(ats_malloc(size)));
160 }
161
162 int
ats_msync(caddr_t addr,size_t len,caddr_t end,int flags)163 ats_msync(caddr_t addr, size_t len, caddr_t end, int flags)
164 {
165 size_t pagesize = ats_pagesize();
166
167 // align start back to page boundary
168 caddr_t a = (caddr_t)(((uintptr_t)addr) & ~(pagesize - 1));
169 // align length to page boundary covering region
170 size_t l = (len + (addr - a) + (pagesize - 1)) & ~(pagesize - 1);
171 if ((a + l) > end) {
172 l = end - a; // strict limit
173 }
174 #if defined(linux)
175 /* Fix INKqa06500
176 Under Linux, msync(..., MS_SYNC) calls are painfully slow, even on
177 non-dirty buffers. This is true as of kernel 2.2.12. We sacrifice
178 restartability under OS in order to avoid a nasty performance hit
179 from a kernel global lock. */
180 #if 0
181 // this was long long ago
182 if (flags & MS_SYNC)
183 flags = (flags & ~MS_SYNC) | MS_ASYNC;
184 #endif
185 #endif
186 int res = msync(a, l, flags);
187 return res;
188 }
189
190 int
ats_madvise(caddr_t addr,size_t len,int flags)191 ats_madvise(caddr_t addr, size_t len, int flags)
192 {
193 #if HAVE_POSIX_MADVISE
194 return posix_madvise(addr, len, flags);
195 #else
196 return madvise(addr, len, flags);
197 #endif
198 }
199
200 int
ats_mlock(caddr_t addr,size_t len)201 ats_mlock(caddr_t addr, size_t len)
202 {
203 size_t pagesize = ats_pagesize();
204
205 caddr_t a = (caddr_t)(((uintptr_t)addr) & ~(pagesize - 1));
206 size_t l = (len + (addr - a) + pagesize - 1) & ~(pagesize - 1);
207 int res = mlock(a, l);
208 return res;
209 }
210
211 void *
ats_track_malloc(size_t size,uint64_t * stat)212 ats_track_malloc(size_t size, uint64_t *stat)
213 {
214 void *ptr = ats_malloc(size);
215 #ifdef HAVE_MALLOC_USABLE_SIZE
216 ink_atomic_increment(stat, malloc_usable_size(ptr));
217 #endif
218 return ptr;
219 }
220
221 void *
ats_track_realloc(void * ptr,size_t size,uint64_t * alloc_stat,uint64_t * free_stat)222 ats_track_realloc(void *ptr, size_t size, uint64_t *alloc_stat, uint64_t *free_stat)
223 {
224 #ifdef HAVE_MALLOC_USABLE_SIZE
225 const size_t old_size = malloc_usable_size(ptr);
226 ptr = ats_realloc(ptr, size);
227 const size_t new_size = malloc_usable_size(ptr);
228 if (old_size < new_size) {
229 // allocating something bigger
230 ink_atomic_increment(alloc_stat, new_size - old_size);
231 } else if (old_size > new_size) {
232 ink_atomic_increment(free_stat, old_size - new_size);
233 }
234 return ptr;
235 #else
236 return ats_realloc(ptr, size);
237 #endif
238 }
239
240 void
ats_track_free(void * ptr,uint64_t * stat)241 ats_track_free(void *ptr, uint64_t *stat)
242 {
243 if (ptr == nullptr) {
244 return;
245 }
246
247 #ifdef HAVE_MALLOC_USABLE_SIZE
248 ink_atomic_increment(stat, malloc_usable_size(ptr));
249 #endif
250 ats_free(ptr);
251 }
252
253 /*-------------------------------------------------------------------------
254 Moved from old ink_resource.h
255 -------------------------------------------------------------------------*/
256 char *
_xstrdup(const char * str,int length,const char *)257 _xstrdup(const char *str, int length, const char * /* path ATS_UNUSED */)
258 {
259 char *newstr;
260
261 if (likely(str)) {
262 if (length < 0) {
263 length = strlen(str);
264 }
265
266 newstr = static_cast<char *>(ats_malloc(length + 1));
267 // If this is a zero length string just null terminate and return.
268 if (unlikely(length == 0)) {
269 *newstr = '\0';
270 } else {
271 strncpy(newstr, str, length); // we cannot do length + 1 because the string isn't
272 newstr[length] = '\0'; // guaranteed to be null terminated!
273 }
274 return newstr;
275 }
276 return nullptr;
277 }
278
279 ats_scoped_str &
operator =(std::string_view s)280 ats_scoped_str::operator=(std::string_view s)
281 {
282 this->clear();
283 if (!s.empty()) {
284 _r = static_cast<char *>(ats_malloc(s.size() + 1));
285 memcpy(_r, s.data(), s.size());
286 _r[s.size()] = '\0';
287 }
288 return *this;
289 }
290