"Fossies" - the Fresh Open Source Software Archive

Member "wrk-4.2.0/src/zmalloc.c" (7 Feb 2021, 12629 Bytes) of package /linux/www/wrk-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zmalloc.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 4.0.2_vs_4.1.0.

    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 #include "atomicvar.h"
   47 
   48 #ifdef HAVE_MALLOC_SIZE
   49 #define PREFIX_SIZE (0)
   50 #else
   51 #if defined(__sun) || defined(__sparc) || defined(__sparc__)
   52 #define PREFIX_SIZE (sizeof(long long))
   53 #else
   54 #define PREFIX_SIZE (sizeof(size_t))
   55 #endif
   56 #endif
   57 
   58 /* Explicitly override malloc/free etc when using tcmalloc. */
   59 #if defined(USE_TCMALLOC)
   60 #define malloc(size) tc_malloc(size)
   61 #define calloc(count,size) tc_calloc(count,size)
   62 #define realloc(ptr,size) tc_realloc(ptr,size)
   63 #define free(ptr) tc_free(ptr)
   64 #elif defined(USE_JEMALLOC)
   65 #define malloc(size) je_malloc(size)
   66 #define calloc(count,size) je_calloc(count,size)
   67 #define realloc(ptr,size) je_realloc(ptr,size)
   68 #define free(ptr) je_free(ptr)
   69 #define mallocx(size,flags) je_mallocx(size,flags)
   70 #define dallocx(ptr,flags) je_dallocx(ptr,flags)
   71 #endif
   72 
   73 #define update_zmalloc_stat_alloc(__n) do { \
   74     size_t _n = (__n); \
   75     if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
   76     atomicIncr(used_memory,__n); \
   77 } while(0)
   78 
   79 #define update_zmalloc_stat_free(__n) do { \
   80     size_t _n = (__n); \
   81     if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
   82     atomicDecr(used_memory,__n); \
   83 } while(0)
   84 
   85 static size_t used_memory = 0;
   86 pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
   87 
   88 static void zmalloc_default_oom(size_t size) {
   89     fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
   90         size);
   91     fflush(stderr);
   92     abort();
   93 }
   94 
   95 static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
   96 
   97 void *zmalloc(size_t size) {
   98     void *ptr = malloc(size+PREFIX_SIZE);
   99 
  100     if (!ptr) zmalloc_oom_handler(size);
  101 #ifdef HAVE_MALLOC_SIZE
  102     update_zmalloc_stat_alloc(zmalloc_size(ptr));
  103     return ptr;
  104 #else
  105     *((size_t*)ptr) = size;
  106     update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  107     return (char*)ptr+PREFIX_SIZE;
  108 #endif
  109 }
  110 
  111 /* Allocation and free functions that bypass the thread cache
  112  * and go straight to the allocator arena bins.
  113  * Currently implemented only for jemalloc. Used for online defragmentation. */
  114 #ifdef HAVE_DEFRAG
  115 void *zmalloc_no_tcache(size_t size) {
  116     void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
  117     if (!ptr) zmalloc_oom_handler(size);
  118     update_zmalloc_stat_alloc(zmalloc_size(ptr));
  119     return ptr;
  120 }
  121 
  122 void zfree_no_tcache(void *ptr) {
  123     if (ptr == NULL) return;
  124     update_zmalloc_stat_free(zmalloc_size(ptr));
  125     dallocx(ptr, MALLOCX_TCACHE_NONE);
  126 }
  127 #endif
  128 
  129 void *zcalloc(size_t size) {
  130     void *ptr = calloc(1, size+PREFIX_SIZE);
  131 
  132     if (!ptr) zmalloc_oom_handler(size);
  133 #ifdef HAVE_MALLOC_SIZE
  134     update_zmalloc_stat_alloc(zmalloc_size(ptr));
  135     return ptr;
  136 #else
  137     *((size_t*)ptr) = size;
  138     update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  139     return (char*)ptr+PREFIX_SIZE;
  140 #endif
  141 }
  142 
  143 void *zrealloc(void *ptr, size_t size) {
  144 #ifndef HAVE_MALLOC_SIZE
  145     void *realptr;
  146 #endif
  147     size_t oldsize;
  148     void *newptr;
  149 
  150     if (ptr == NULL) return zmalloc(size);
  151 #ifdef HAVE_MALLOC_SIZE
  152     oldsize = zmalloc_size(ptr);
  153     newptr = realloc(ptr,size);
  154     if (!newptr) zmalloc_oom_handler(size);
  155 
  156     update_zmalloc_stat_free(oldsize);
  157     update_zmalloc_stat_alloc(zmalloc_size(newptr));
  158     return newptr;
  159 #else
  160     realptr = (char*)ptr-PREFIX_SIZE;
  161     oldsize = *((size_t*)realptr);
  162     newptr = realloc(realptr,size+PREFIX_SIZE);
  163     if (!newptr) zmalloc_oom_handler(size);
  164 
  165     *((size_t*)newptr) = size;
  166     update_zmalloc_stat_free(oldsize);
  167     update_zmalloc_stat_alloc(size);
  168     return (char*)newptr+PREFIX_SIZE;
  169 #endif
  170 }
  171 
  172 /* Provide zmalloc_size() for systems where this function is not provided by
  173  * malloc itself, given that in that case we store a header with this
  174  * information as the first bytes of every allocation. */
  175 #ifndef HAVE_MALLOC_SIZE
  176 size_t zmalloc_size(void *ptr) {
  177     void *realptr = (char*)ptr-PREFIX_SIZE;
  178     size_t size = *((size_t*)realptr);
  179     /* Assume at least that all the allocations are padded at sizeof(long) by
  180      * the underlying allocator. */
  181     if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
  182     return size+PREFIX_SIZE;
  183 }
  184 #endif
  185 
  186 void zfree(void *ptr) {
  187 #ifndef HAVE_MALLOC_SIZE
  188     void *realptr;
  189     size_t oldsize;
  190 #endif
  191 
  192     if (ptr == NULL) return;
  193 #ifdef HAVE_MALLOC_SIZE
  194     update_zmalloc_stat_free(zmalloc_size(ptr));
  195     free(ptr);
  196 #else
  197     realptr = (char*)ptr-PREFIX_SIZE;
  198     oldsize = *((size_t*)realptr);
  199     update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
  200     free(realptr);
  201 #endif
  202 }
  203 
  204 char *zstrdup(const char *s) {
  205     size_t l = strlen(s)+1;
  206     char *p = zmalloc(l);
  207 
  208     memcpy(p,s,l);
  209     return p;
  210 }
  211 
  212 size_t zmalloc_used_memory(void) {
  213     size_t um;
  214     atomicGet(used_memory,um);
  215     return um;
  216 }
  217 
  218 void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
  219     zmalloc_oom_handler = oom_handler;
  220 }
  221 
  222 /* Get the RSS information in an OS-specific way.
  223  *
  224  * WARNING: the function zmalloc_get_rss() is not designed to be fast
  225  * and may not be called in the busy loops where Redis tries to release
  226  * memory expiring or swapping out objects.
  227  *
  228  * For this kind of "fast RSS reporting" usages use instead the
  229  * function RedisEstimateRSS() that is a much faster (and less precise)
  230  * version of the function. */
  231 
  232 #if defined(HAVE_PROC_STAT)
  233 #include <unistd.h>
  234 #include <sys/types.h>
  235 #include <sys/stat.h>
  236 #include <fcntl.h>
  237 
  238 size_t zmalloc_get_rss(void) {
  239     int page = sysconf(_SC_PAGESIZE);
  240     size_t rss;
  241     char buf[4096];
  242     char filename[256];
  243     int fd, count;
  244     char *p, *x;
  245 
  246     snprintf(filename,256,"/proc/%d/stat",getpid());
  247     if ((fd = open(filename,O_RDONLY)) == -1) return 0;
  248     if (read(fd,buf,4096) <= 0) {
  249         close(fd);
  250         return 0;
  251     }
  252     close(fd);
  253 
  254     p = buf;
  255     count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
  256     while(p && count--) {
  257         p = strchr(p,' ');
  258         if (p) p++;
  259     }
  260     if (!p) return 0;
  261     x = strchr(p,' ');
  262     if (!x) return 0;
  263     *x = '\0';
  264 
  265     rss = strtoll(p,NULL,10);
  266     rss *= page;
  267     return rss;
  268 }
  269 #elif defined(HAVE_TASKINFO)
  270 #include <unistd.h>
  271 #include <stdio.h>
  272 #include <stdlib.h>
  273 #include <sys/types.h>
  274 #include <sys/sysctl.h>
  275 #include <mach/task.h>
  276 #include <mach/mach_init.h>
  277 
  278 size_t zmalloc_get_rss(void) {
  279     task_t task = MACH_PORT_NULL;
  280     struct task_basic_info t_info;
  281     mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
  282 
  283     if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
  284         return 0;
  285     task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
  286 
  287     return t_info.resident_size;
  288 }
  289 #else
  290 size_t zmalloc_get_rss(void) {
  291     /* If we can't get the RSS in an OS-specific way for this system just
  292      * return the memory usage we estimated in zmalloc()..
  293      *
  294      * Fragmentation will appear to be always 1 (no fragmentation)
  295      * of course... */
  296     return zmalloc_used_memory();
  297 }
  298 #endif
  299 
  300 /* Fragmentation = RSS / allocated-bytes */
  301 float zmalloc_get_fragmentation_ratio(size_t rss) {
  302     return (float)rss/zmalloc_used_memory();
  303 }
  304 
  305 /* Get the sum of the specified field (converted form kb to bytes) in
  306  * /proc/self/smaps. The field must be specified with trailing ":" as it
  307  * apperas in the smaps output.
  308  *
  309  * If a pid is specified, the information is extracted for such a pid,
  310  * otherwise if pid is -1 the information is reported is about the
  311  * current process.
  312  *
  313  * Example: zmalloc_get_smap_bytes_by_field("Rss:",-1);
  314  */
  315 #if defined(HAVE_PROC_SMAPS)
  316 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
  317     char line[1024];
  318     size_t bytes = 0;
  319     int flen = strlen(field);
  320     FILE *fp;
  321 
  322     if (pid == -1) {
  323         fp = fopen("/proc/self/smaps","r");
  324     } else {
  325         char filename[128];
  326         snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid);
  327         fp = fopen(filename,"r");
  328     }
  329 
  330     if (!fp) return 0;
  331     while(fgets(line,sizeof(line),fp) != NULL) {
  332         if (strncmp(line,field,flen) == 0) {
  333             char *p = strchr(line,'k');
  334             if (p) {
  335                 *p = '\0';
  336                 bytes += strtol(line+flen,NULL,10) * 1024;
  337             }
  338         }
  339     }
  340     fclose(fp);
  341     return bytes;
  342 }
  343 #else
  344 size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
  345     ((void) field);
  346     ((void) pid);
  347     return 0;
  348 }
  349 #endif
  350 
  351 size_t zmalloc_get_private_dirty(long pid) {
  352     return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid);
  353 }
  354 
  355 /* Returns the size of physical memory (RAM) in bytes.
  356  * It looks ugly, but this is the cleanest way to achive cross platform results.
  357  * Cleaned up from:
  358  *
  359  * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
  360  *
  361  * Note that this function:
  362  * 1) Was released under the following CC attribution license:
  363  *    http://creativecommons.org/licenses/by/3.0/deed.en_US.
  364  * 2) Was originally implemented by David Robert Nadeau.
  365  * 3) Was modified for Redis by Matt Stancliff.
  366  * 4) This note exists in order to comply with the original license.
  367  */
  368 size_t zmalloc_get_memory_size(void) {
  369 #if defined(__unix__) || defined(__unix) || defined(unix) || \
  370     (defined(__APPLE__) && defined(__MACH__))
  371 #if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
  372     int mib[2];
  373     mib[0] = CTL_HW;
  374 #if defined(HW_MEMSIZE)
  375     mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */
  376 #elif defined(HW_PHYSMEM64)
  377     mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */
  378 #endif
  379     int64_t size = 0;               /* 64-bit */
  380     size_t len = sizeof(size);
  381     if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)
  382         return (size_t)size;
  383     return 0L;          /* Failed? */
  384 
  385 #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
  386     /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
  387     return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);
  388 
  389 #elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
  390     /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
  391     int mib[2];
  392     mib[0] = CTL_HW;
  393 #if defined(HW_REALMEM)
  394     mib[1] = HW_REALMEM;        /* FreeBSD. ----------------- */
  395 #elif defined(HW_PYSMEM)
  396     mib[1] = HW_PHYSMEM;        /* Others. ------------------ */
  397 #endif
  398     unsigned int size = 0;      /* 32-bit */
  399     size_t len = sizeof(size);
  400     if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
  401         return (size_t)size;
  402     return 0L;          /* Failed? */
  403 #else
  404     return 0L;          /* Unknown method to get the data. */
  405 #endif
  406 #else
  407     return 0L;          /* Unknown OS. */
  408 #endif
  409 }
  410 
  411