xref: /redis-3.2.3/src/zmalloc.c (revision 414c3dea)
1 /* zmalloc - total amount of allocated memory aware version of malloc()
2  *
3  * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   * Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *   * Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *   * Neither the name of Redis nor the names of its contributors may be used
15  *     to endorse or promote products derived from this software without
16  *     specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <pthread.h>
35 #include "config.h"
36 #include "zmalloc.h"
37 
38 #ifdef HAVE_MALLOC_SIZE
39 #define PREFIX_SIZE (0)
40 #else
41 #if defined(__sun) || defined(__sparc) || defined(__sparc__)
42 #define PREFIX_SIZE (sizeof(long long))
43 #else
44 #define PREFIX_SIZE (sizeof(size_t))
45 #endif
46 #endif
47 
48 /* Explicitly override malloc/free etc when using tcmalloc. */
49 #if defined(USE_TCMALLOC)
50 #define malloc(size) tc_malloc(size)
51 #define calloc(count,size) tc_calloc(count,size)
52 #define realloc(ptr,size) tc_realloc(ptr,size)
53 #define free(ptr) tc_free(ptr)
54 #elif defined(USE_JEMALLOC)
55 #define malloc(size) je_malloc(size)
56 #define calloc(count,size) je_calloc(count,size)
57 #define realloc(ptr,size) je_realloc(ptr,size)
58 #define free(ptr) je_free(ptr)
59 #endif
60 
61 #define update_zmalloc_stat_alloc(__n,__size) do { \
62     size_t _n = (__n); \
63     if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
64     if (zmalloc_thread_safe) { \
65         pthread_mutex_lock(&used_memory_mutex);  \
66         used_memory += _n; \
67         pthread_mutex_unlock(&used_memory_mutex); \
68     } else { \
69         used_memory += _n; \
70     } \
71 } while(0)
72 
73 #define update_zmalloc_stat_free(__n) do { \
74     size_t _n = (__n); \
75     if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
76     if (zmalloc_thread_safe) { \
77         pthread_mutex_lock(&used_memory_mutex);  \
78         used_memory -= _n; \
79         pthread_mutex_unlock(&used_memory_mutex); \
80     } else { \
81         used_memory -= _n; \
82     } \
83 } while(0)
84 
85 static size_t used_memory = 0;
86 static int zmalloc_thread_safe = 0;
87 pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
88 
89 static void zmalloc_oom(size_t size) {
90     fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
91         size);
92     fflush(stderr);
93     abort();
94 }
95 
96 void *zmalloc(size_t size) {
97     void *ptr = malloc(size+PREFIX_SIZE);
98 
99     if (!ptr) zmalloc_oom(size);
100 #ifdef HAVE_MALLOC_SIZE
101     update_zmalloc_stat_alloc(zmalloc_size(ptr),size);
102     return ptr;
103 #else
104     *((size_t*)ptr) = size;
105     update_zmalloc_stat_alloc(size+PREFIX_SIZE,size);
106     return (char*)ptr+PREFIX_SIZE;
107 #endif
108 }
109 
110 void *zcalloc(size_t size) {
111     void *ptr = calloc(1, size+PREFIX_SIZE);
112 
113     if (!ptr) zmalloc_oom(size);
114 #ifdef HAVE_MALLOC_SIZE
115     update_zmalloc_stat_alloc(zmalloc_size(ptr),size);
116     return ptr;
117 #else
118     *((size_t*)ptr) = size;
119     update_zmalloc_stat_alloc(size+PREFIX_SIZE,size);
120     return (char*)ptr+PREFIX_SIZE;
121 #endif
122 }
123 
124 void *zrealloc(void *ptr, size_t size) {
125 #ifndef HAVE_MALLOC_SIZE
126     void *realptr;
127 #endif
128     size_t oldsize;
129     void *newptr;
130 
131     if (ptr == NULL) return zmalloc(size);
132 #ifdef HAVE_MALLOC_SIZE
133     oldsize = zmalloc_size(ptr);
134     newptr = realloc(ptr,size);
135     if (!newptr) zmalloc_oom(size);
136 
137     update_zmalloc_stat_free(oldsize);
138     update_zmalloc_stat_alloc(zmalloc_size(newptr),size);
139     return newptr;
140 #else
141     realptr = (char*)ptr-PREFIX_SIZE;
142     oldsize = *((size_t*)realptr);
143     newptr = realloc(realptr,size+PREFIX_SIZE);
144     if (!newptr) zmalloc_oom(size);
145 
146     *((size_t*)newptr) = size;
147     update_zmalloc_stat_free(oldsize);
148     update_zmalloc_stat_alloc(size,size);
149     return (char*)newptr+PREFIX_SIZE;
150 #endif
151 }
152 
153 void zfree(void *ptr) {
154 #ifndef HAVE_MALLOC_SIZE
155     void *realptr;
156     size_t oldsize;
157 #endif
158 
159     if (ptr == NULL) return;
160 #ifdef HAVE_MALLOC_SIZE
161     update_zmalloc_stat_free(zmalloc_size(ptr));
162     free(ptr);
163 #else
164     realptr = (char*)ptr-PREFIX_SIZE;
165     oldsize = *((size_t*)realptr);
166     update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
167     free(realptr);
168 #endif
169 }
170 
171 char *zstrdup(const char *s) {
172     size_t l = strlen(s)+1;
173     char *p = zmalloc(l);
174 
175     memcpy(p,s,l);
176     return p;
177 }
178 
179 size_t zmalloc_used_memory(void) {
180     size_t um;
181 
182     if (zmalloc_thread_safe) pthread_mutex_lock(&used_memory_mutex);
183     um = used_memory;
184     if (zmalloc_thread_safe) pthread_mutex_unlock(&used_memory_mutex);
185     return um;
186 }
187 
188 void zmalloc_enable_thread_safeness(void) {
189     zmalloc_thread_safe = 1;
190 }
191 
192 /* Get the RSS information in an OS-specific way.
193  *
194  * WARNING: the function zmalloc_get_rss() is not designed to be fast
195  * and may not be called in the busy loops where Redis tries to release
196  * memory expiring or swapping out objects.
197  *
198  * For this kind of "fast RSS reporting" usages use instead the
199  * function RedisEstimateRSS() that is a much faster (and less precise)
200  * version of the funciton. */
201 
202 #if defined(HAVE_PROCFS)
203 #include <unistd.h>
204 #include <sys/types.h>
205 #include <sys/stat.h>
206 #include <fcntl.h>
207 
208 size_t zmalloc_get_rss(void) {
209     int page = sysconf(_SC_PAGESIZE);
210     size_t rss;
211     char buf[4096];
212     char filename[256];
213     int fd, count;
214     char *p, *x;
215 
216     snprintf(filename,256,"/proc/%d/stat",getpid());
217     if ((fd = open(filename,O_RDONLY)) == -1) return 0;
218     if (read(fd,buf,4096) <= 0) {
219         close(fd);
220         return 0;
221     }
222     close(fd);
223 
224     p = buf;
225     count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
226     while(p && count--) {
227         p = strchr(p,' ');
228         if (p) p++;
229     }
230     if (!p) return 0;
231     x = strchr(p,' ');
232     if (!x) return 0;
233     *x = '\0';
234 
235     rss = strtoll(p,NULL,10);
236     rss *= page;
237     return rss;
238 }
239 #elif defined(HAVE_TASKINFO)
240 #include <unistd.h>
241 #include <stdio.h>
242 #include <stdlib.h>
243 #include <sys/types.h>
244 #include <sys/sysctl.h>
245 #include <mach/task.h>
246 #include <mach/mach_init.h>
247 
248 size_t zmalloc_get_rss(void) {
249     task_t task = MACH_PORT_NULL;
250     struct task_basic_info t_info;
251     mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
252 
253     if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
254         return 0;
255     task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
256 
257     return t_info.resident_size;
258 }
259 #else
260 size_t zmalloc_get_rss(void) {
261     /* If we can't get the RSS in an OS-specific way for this system just
262      * return the memory usage we estimated in zmalloc()..
263      *
264      * Fragmentation will appear to be always 1 (no fragmentation)
265      * of course... */
266     return zmalloc_used_memory();
267 }
268 #endif
269 
270 /* Fragmentation = RSS / allocated-bytes */
271 float zmalloc_get_fragmentation_ratio(void) {
272     return (float)zmalloc_get_rss()/zmalloc_used_memory();
273 }
274