Mercurial
comparison third_party/wrk/src/zmalloc.c @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | 94705b5986b3 |
| children |
comparison
equal
deleted
inserted
replaced
| 176:fed99fc04e12 | 186:8cf4ec5e2191 |
|---|---|
| 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 |