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 34 /* This function provide us access to the original libc free(). This is useful 35 * for instance to free results obtained by backtrace_symbols(). We need 36 * to define this function before including zmalloc.h that may shadow the 37 * free implementation if we use jemalloc or another non standard allocator. */ 38 void zlibc_free(void *ptr) { 39 free(ptr); 40 } 41 42 #include <string.h> 43 #include <pthread.h> 44 #include "config.h" 45 #include "zmalloc.h" 46 47 #ifdef HAVE_MALLOC_SIZE 48 #define PREFIX_SIZE (0) 49 #else 50 #if defined(__sun) || defined(__sparc) || defined(__sparc__) 51 #define PREFIX_SIZE (sizeof(long long)) 52 #else 53 #define PREFIX_SIZE (sizeof(size_t)) 54 #endif 55 #endif 56 57 /* Explicitly override malloc/free etc when using tcmalloc. */ 58 #if defined(USE_TCMALLOC) 59 #define malloc(size) tc_malloc(size) 60 #define calloc(count,size) tc_calloc(count,size) 61 #define realloc(ptr,size) tc_realloc(ptr,size) 62 #define free(ptr) tc_free(ptr) 63 #elif defined(USE_JEMALLOC) 64 #define malloc(size) je_malloc(size) 65 #define calloc(count,size) je_calloc(count,size) 66 #define realloc(ptr,size) je_realloc(ptr,size) 67 #define free(ptr) je_free(ptr) 68 #endif 69 70 #if defined(__ATOMIC_RELAXED) 71 #define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 72 #define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 73 #elif defined(HAVE_ATOMIC) 74 #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n)) 75 #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n)) 76 #else 77 #define update_zmalloc_stat_add(__n) do { \ 78 pthread_mutex_lock(&used_memory_mutex); \ 79 used_memory += (__n); \ 80 pthread_mutex_unlock(&used_memory_mutex); \ 81 } while(0) 82 83 #define update_zmalloc_stat_sub(__n) do { \ 84 pthread_mutex_lock(&used_memory_mutex); \ 85 used_memory -= (__n); \ 86 pthread_mutex_unlock(&used_memory_mutex); \ 87 } while(0) 88 89 #endif 90 91 #define update_zmalloc_stat_alloc(__n) do { \ 92 size_t _n = (__n); \ 93 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 94 if (zmalloc_thread_safe) { \ 95 update_zmalloc_stat_add(_n); \ 96 } else { \ 97 used_memory += _n; \ 98 } \ 99 } while(0) 100 101 #define update_zmalloc_stat_free(__n) do { \ 102 size_t _n = (__n); \ 103 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 104 if (zmalloc_thread_safe) { \ 105 update_zmalloc_stat_sub(_n); \ 106 } else { \ 107 used_memory -= _n; \ 108 } \ 109 } while(0) 110 111 static size_t used_memory = 0; 112 static int zmalloc_thread_safe = 0; 113 pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; 114 115 static void zmalloc_default_oom(size_t size) { 116 fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", 117 size); 118 fflush(stderr); 119 abort(); 120 } 121 122 static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom; 123 124 void *zmalloc(size_t size) { 125 void *ptr = malloc(size+PREFIX_SIZE); 126 127 if (!ptr) zmalloc_oom_handler(size); 128 #ifdef HAVE_MALLOC_SIZE 129 update_zmalloc_stat_alloc(zmalloc_size(ptr)); 130 return ptr; 131 #else 132 *((size_t*)ptr) = size; 133 update_zmalloc_stat_alloc(size+PREFIX_SIZE); 134 return (char*)ptr+PREFIX_SIZE; 135 #endif 136 } 137 138 void *zcalloc(size_t size) { 139 void *ptr = calloc(1, size+PREFIX_SIZE); 140 141 if (!ptr) zmalloc_oom_handler(size); 142 #ifdef HAVE_MALLOC_SIZE 143 update_zmalloc_stat_alloc(zmalloc_size(ptr)); 144 return ptr; 145 #else 146 *((size_t*)ptr) = size; 147 update_zmalloc_stat_alloc(size+PREFIX_SIZE); 148 return (char*)ptr+PREFIX_SIZE; 149 #endif 150 } 151 152 void *zrealloc(void *ptr, size_t size) { 153 #ifndef HAVE_MALLOC_SIZE 154 void *realptr; 155 #endif 156 size_t oldsize; 157 void *newptr; 158 159 if (ptr == NULL) return zmalloc(size); 160 #ifdef HAVE_MALLOC_SIZE 161 oldsize = zmalloc_size(ptr); 162 newptr = realloc(ptr,size); 163 if (!newptr) zmalloc_oom_handler(size); 164 165 update_zmalloc_stat_free(oldsize); 166 update_zmalloc_stat_alloc(zmalloc_size(newptr)); 167 return newptr; 168 #else 169 realptr = (char*)ptr-PREFIX_SIZE; 170 oldsize = *((size_t*)realptr); 171 newptr = realloc(realptr,size+PREFIX_SIZE); 172 if (!newptr) zmalloc_oom_handler(size); 173 174 *((size_t*)newptr) = size; 175 update_zmalloc_stat_free(oldsize); 176 update_zmalloc_stat_alloc(size); 177 return (char*)newptr+PREFIX_SIZE; 178 #endif 179 } 180 181 /* Provide zmalloc_size() for systems where this function is not provided by 182 * malloc itself, given that in that case we store a header with this 183 * information as the first bytes of every allocation. */ 184 #ifndef HAVE_MALLOC_SIZE 185 size_t zmalloc_size(void *ptr) { 186 void *realptr = (char*)ptr-PREFIX_SIZE; 187 size_t size = *((size_t*)realptr); 188 /* Assume at least that all the allocations are padded at sizeof(long) by 189 * the underlying allocator. */ 190 if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1)); 191 return size+PREFIX_SIZE; 192 } 193 #endif 194 195 void zfree(void *ptr) { 196 #ifndef HAVE_MALLOC_SIZE 197 void *realptr; 198 size_t oldsize; 199 #endif 200 201 if (ptr == NULL) return; 202 #ifdef HAVE_MALLOC_SIZE 203 update_zmalloc_stat_free(zmalloc_size(ptr)); 204 free(ptr); 205 #else 206 realptr = (char*)ptr-PREFIX_SIZE; 207 oldsize = *((size_t*)realptr); 208 update_zmalloc_stat_free(oldsize+PREFIX_SIZE); 209 free(realptr); 210 #endif 211 } 212 213 char *zstrdup(const char *s) { 214 size_t l = strlen(s)+1; 215 char *p = zmalloc(l); 216 217 memcpy(p,s,l); 218 return p; 219 } 220 221 size_t zmalloc_used_memory(void) { 222 size_t um; 223 224 if (zmalloc_thread_safe) { 225 #if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC) 226 um = update_zmalloc_stat_add(0); 227 #else 228 pthread_mutex_lock(&used_memory_mutex); 229 um = used_memory; 230 pthread_mutex_unlock(&used_memory_mutex); 231 #endif 232 } 233 else { 234 um = used_memory; 235 } 236 237 return um; 238 } 239 240 void zmalloc_enable_thread_safeness(void) { 241 zmalloc_thread_safe = 1; 242 } 243 244 void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) { 245 zmalloc_oom_handler = oom_handler; 246 } 247 248 /* Get the RSS information in an OS-specific way. 249 * 250 * WARNING: the function zmalloc_get_rss() is not designed to be fast 251 * and may not be called in the busy loops where Redis tries to release 252 * memory expiring or swapping out objects. 253 * 254 * For this kind of "fast RSS reporting" usages use instead the 255 * function RedisEstimateRSS() that is a much faster (and less precise) 256 * version of the function. */ 257 258 #if defined(HAVE_PROC_STAT) 259 #include <unistd.h> 260 #include <sys/types.h> 261 #include <sys/stat.h> 262 #include <fcntl.h> 263 264 size_t zmalloc_get_rss(void) { 265 int page = sysconf(_SC_PAGESIZE); 266 size_t rss; 267 char buf[4096]; 268 char filename[256]; 269 int fd, count; 270 char *p, *x; 271 272 snprintf(filename,256,"/proc/%d/stat",getpid()); 273 if ((fd = open(filename,O_RDONLY)) == -1) return 0; 274 if (read(fd,buf,4096) <= 0) { 275 close(fd); 276 return 0; 277 } 278 close(fd); 279 280 p = buf; 281 count = 23; /* RSS is the 24th field in /proc/<pid>/stat */ 282 while(p && count--) { 283 p = strchr(p,' '); 284 if (p) p++; 285 } 286 if (!p) return 0; 287 x = strchr(p,' '); 288 if (!x) return 0; 289 *x = '\0'; 290 291 rss = strtoll(p,NULL,10); 292 rss *= page; 293 return rss; 294 } 295 #elif defined(HAVE_TASKINFO) 296 #include <unistd.h> 297 #include <stdio.h> 298 #include <stdlib.h> 299 #include <sys/types.h> 300 #include <sys/sysctl.h> 301 #include <mach/task.h> 302 #include <mach/mach_init.h> 303 304 size_t zmalloc_get_rss(void) { 305 task_t task = MACH_PORT_NULL; 306 struct task_basic_info t_info; 307 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; 308 309 if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) 310 return 0; 311 task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); 312 313 return t_info.resident_size; 314 } 315 #else 316 size_t zmalloc_get_rss(void) { 317 /* If we can't get the RSS in an OS-specific way for this system just 318 * return the memory usage we estimated in zmalloc().. 319 * 320 * Fragmentation will appear to be always 1 (no fragmentation) 321 * of course... */ 322 return zmalloc_used_memory(); 323 } 324 #endif 325 326 /* Fragmentation = RSS / allocated-bytes */ 327 float zmalloc_get_fragmentation_ratio(size_t rss) { 328 return (float)rss/zmalloc_used_memory(); 329 } 330 331 #if defined(HAVE_PROC_SMAPS) 332 size_t zmalloc_get_private_dirty(void) { 333 char line[1024]; 334 size_t pd = 0; 335 FILE *fp = fopen("/proc/self/smaps","r"); 336 337 if (!fp) return 0; 338 while(fgets(line,sizeof(line),fp) != NULL) { 339 if (strncmp(line,"Private_Dirty:",14) == 0) { 340 char *p = strchr(line,'k'); 341 if (p) { 342 *p = '\0'; 343 pd += strtol(line+14,NULL,10) * 1024; 344 } 345 } 346 } 347 fclose(fp); 348 return pd; 349 } 350 #else 351 size_t zmalloc_get_private_dirty(void) { 352 return 0; 353 } 354 #endif 355