xref: /trafficserver/src/tscore/ink_memory.cc (revision 4cfd5a73)
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)
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 void
ats_memalign_free(void * ptr)135 ats_memalign_free(void *ptr)
136 {
137   if (likely(ptr)) {
138     free(ptr);
139   }
140 }
141 
142 // This effectively makes mallopt() a no-op (currently) when tcmalloc
143 // or jemalloc is used. This might break our usage for increasing the
144 // number of mmap areas (ToDo: Do we still really need that??).
145 //
146 // TODO: I think we might be able to get rid of this?
147 int
ats_mallopt(int param ATS_UNUSED,int value ATS_UNUSED)148 ats_mallopt(int param ATS_UNUSED, int value ATS_UNUSED)
149 {
150 #if TS_HAS_JEMALLOC
151 // TODO: jemalloc code ?
152 #else
153 #if TS_HAS_TCMALLOC
154 // TODO: tcmalloc code ?
155 #else
156 #if defined(__GLIBC__)
157   return mallopt(param, value);
158 #endif // ! defined(__GLIBC__)
159 #endif // ! TS_HAS_TCMALLOC
160 #endif // ! TS_HAS_JEMALLOC
161   return 0;
162 }
163 
164 ats_unique_buf
ats_unique_malloc(size_t size)165 ats_unique_malloc(size_t size)
166 {
167   return ats_unique_buf(static_cast<uint8_t *>(ats_malloc(size)));
168 }
169 
170 int
ats_msync(caddr_t addr,size_t len,caddr_t end,int flags)171 ats_msync(caddr_t addr, size_t len, caddr_t end, int flags)
172 {
173   size_t pagesize = ats_pagesize();
174 
175   // align start back to page boundary
176   caddr_t a = (caddr_t)(((uintptr_t)addr) & ~(pagesize - 1));
177   // align length to page boundary covering region
178   size_t l = (len + (addr - a) + (pagesize - 1)) & ~(pagesize - 1);
179   if ((a + l) > end) {
180     l = end - a; // strict limit
181   }
182 #if defined(linux)
183 /* Fix INKqa06500
184    Under Linux, msync(..., MS_SYNC) calls are painfully slow, even on
185    non-dirty buffers. This is true as of kernel 2.2.12. We sacrifice
186    restartability under OS in order to avoid a nasty performance hit
187    from a kernel global lock. */
188 #if 0
189   // this was long long ago
190   if (flags & MS_SYNC)
191     flags = (flags & ~MS_SYNC) | MS_ASYNC;
192 #endif
193 #endif
194   int res = msync(a, l, flags);
195   return res;
196 }
197 
198 int
ats_madvise(caddr_t addr,size_t len,int flags)199 ats_madvise(caddr_t addr, size_t len, int flags)
200 {
201 #if HAVE_POSIX_MADVISE
202   return posix_madvise(addr, len, flags);
203 #else
204   return madvise(addr, len, flags);
205 #endif
206 }
207 
208 int
ats_mlock(caddr_t addr,size_t len)209 ats_mlock(caddr_t addr, size_t len)
210 {
211   size_t pagesize = ats_pagesize();
212 
213   caddr_t a = (caddr_t)(((uintptr_t)addr) & ~(pagesize - 1));
214   size_t l  = (len + (addr - a) + pagesize - 1) & ~(pagesize - 1);
215   int res   = mlock(a, l);
216   return res;
217 }
218 
219 void *
ats_track_malloc(size_t size,uint64_t * stat)220 ats_track_malloc(size_t size, uint64_t *stat)
221 {
222   void *ptr = ats_malloc(size);
223 #ifdef HAVE_MALLOC_USABLE_SIZE
224   ink_atomic_increment(stat, malloc_usable_size(ptr));
225 #endif
226   return ptr;
227 }
228 
229 void *
ats_track_realloc(void * ptr,size_t size,uint64_t * alloc_stat,uint64_t * free_stat)230 ats_track_realloc(void *ptr, size_t size, uint64_t *alloc_stat, uint64_t *free_stat)
231 {
232 #ifdef HAVE_MALLOC_USABLE_SIZE
233   const size_t old_size = malloc_usable_size(ptr);
234   ptr                   = ats_realloc(ptr, size);
235   const size_t new_size = malloc_usable_size(ptr);
236   if (old_size < new_size) {
237     // allocating something bigger
238     ink_atomic_increment(alloc_stat, new_size - old_size);
239   } else if (old_size > new_size) {
240     ink_atomic_increment(free_stat, old_size - new_size);
241   }
242   return ptr;
243 #else
244   return ats_realloc(ptr, size);
245 #endif
246 }
247 
248 void
ats_track_free(void * ptr,uint64_t * stat)249 ats_track_free(void *ptr, uint64_t *stat)
250 {
251   if (ptr == nullptr) {
252     return;
253   }
254 
255 #ifdef HAVE_MALLOC_USABLE_SIZE
256   ink_atomic_increment(stat, malloc_usable_size(ptr));
257 #endif
258   ats_free(ptr);
259 }
260 
261 /*-------------------------------------------------------------------------
262   Moved from old ink_resource.h
263   -------------------------------------------------------------------------*/
264 char *
_xstrdup(const char * str,int length,const char *)265 _xstrdup(const char *str, int length, const char * /* path ATS_UNUSED */)
266 {
267   char *newstr;
268 
269   if (likely(str)) {
270     if (length < 0) {
271       length = strlen(str);
272     }
273 
274     newstr = static_cast<char *>(ats_malloc(length + 1));
275     // If this is a zero length string just null terminate and return.
276     if (unlikely(length == 0)) {
277       *newstr = '\0';
278     } else {
279       strncpy(newstr, str, length); // we cannot do length + 1 because the string isn't
280       newstr[length] = '\0';        // guaranteed to be null terminated!
281     }
282     return newstr;
283   }
284   return nullptr;
285 }
286 
287 ats_scoped_str &
operator =(std::string_view s)288 ats_scoped_str::operator=(std::string_view s)
289 {
290   this->clear();
291   if (!s.empty()) {
292     _r = static_cast<char *>(ats_malloc(s.size() + 1));
293     memcpy(_r, s.data(), s.size());
294     _r[s.size()] = '\0';
295   }
296   return *this;
297 }
298