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