meminfo.c (3954B)
1 /* Copyright (c) 2003-2004, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file meminfo.c 8 * 9 * \brief Functions to query total memory, and access meta-information about 10 * the allocator. 11 **/ 12 13 #include "lib/meminfo/meminfo.h" 14 15 #include "lib/cc/compat_compiler.h" 16 #include "lib/cc/torint.h" 17 #include "lib/fs/files.h" 18 #include "lib/log/log.h" 19 #include "lib/malloc/malloc.h" 20 #include "lib/string/util_string.h" 21 22 #ifdef HAVE_FCNTL_H 23 #include <fcntl.h> 24 #endif 25 #ifdef HAVE_MALLOC_H 26 #include <malloc.h> 27 #endif 28 #ifdef HAVE_UNISTD_H 29 #include <unistd.h> 30 #endif 31 32 #ifdef _WIN32 33 #include <windows.h> 34 #endif 35 #include <string.h> 36 37 #if defined(HAVE_SYS_SYSCTL_H) && !defined(_WIN32) && !defined(__linux__) 38 #include <sys/sysctl.h> 39 #endif 40 41 #if defined(HW_PHYSMEM64) 42 /* OpenBSD and NetBSD define this */ 43 #define INT64_HW_MEM HW_PHYSMEM64 44 #elif defined(HW_MEMSIZE) 45 /* OSX defines this one */ 46 #define INT64_HW_MEM HW_MEMSIZE 47 #endif /* defined(HW_PHYSMEM64) || ... */ 48 49 /** 50 * Helper: try to detect the total system memory, and return it. On failure, 51 * return 0. 52 */ 53 static uint64_t 54 get_total_system_memory_impl(void) 55 { 56 #if defined(__linux__) 57 /* On linux, sysctl is deprecated. Because proc is so awesome that you 58 * shouldn't _want_ to write portable code, I guess? */ 59 unsigned long long result=0; 60 int fd = -1; 61 char *s = NULL; 62 const char *cp; 63 size_t file_size=0; 64 if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0))) 65 return 0; 66 s = read_file_to_str_until_eof(fd, 65536, &file_size); 67 if (!s) 68 goto err; 69 cp = find_str_at_start_of_line(s, "MemTotal:"); 70 if (!cp) 71 goto err; 72 /* Use the system sscanf so that space will match a wider number of space */ 73 if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1) 74 goto err; 75 76 close(fd); 77 tor_free(s); 78 return result * 1024; 79 80 /* LCOV_EXCL_START Can't reach this unless proc is broken. */ 81 err: 82 tor_free(s); 83 close(fd); 84 return 0; 85 /* LCOV_EXCL_STOP */ 86 #elif defined (_WIN32) 87 /* Windows has MEMORYSTATUSEX; pretty straightforward. */ 88 MEMORYSTATUSEX ms; 89 memset(&ms, 0, sizeof(ms)); 90 ms.dwLength = sizeof(ms); 91 if (! GlobalMemoryStatusEx(&ms)) 92 return 0; 93 94 return ms.ullTotalPhys; 95 96 #elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM) 97 /* On many systems, HW_PHYSMEM is clipped to 32 bits; let's use a better 98 * variant if we know about it. */ 99 uint64_t memsize = 0; 100 size_t len = sizeof(memsize); 101 int mib[2] = {CTL_HW, INT64_HW_MEM}; 102 if (sysctl(mib,2,&memsize,&len,NULL,0)) 103 return 0; 104 105 return memsize; 106 107 #elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM) 108 /* On some systems (like FreeBSD I hope) you can use a size_t with 109 * HW_PHYSMEM. */ 110 size_t memsize=0; 111 size_t len = sizeof(memsize); 112 int mib[2] = {CTL_HW, HW_PHYSMEM}; 113 if (sysctl(mib,2,&memsize,&len,NULL,0)) 114 return 0; 115 116 return memsize; 117 118 #else 119 /* I have no clue. */ 120 return 0; 121 #endif /* defined(__linux__) || ... */ 122 } 123 124 /** 125 * Try to find out how much physical memory the system has. On success, 126 * return 0 and set *<b>mem_out</b> to that value. On failure, return -1. 127 */ 128 MOCK_IMPL(int, 129 get_total_system_memory, (size_t *mem_out)) 130 { 131 static size_t mem_cached=0; 132 uint64_t m = get_total_system_memory_impl(); 133 if (0 == m) { 134 /* LCOV_EXCL_START -- can't make this happen without mocking. */ 135 /* We couldn't find our memory total */ 136 if (0 == mem_cached) { 137 /* We have no cached value either */ 138 *mem_out = 0; 139 return -1; 140 } 141 142 *mem_out = mem_cached; 143 return 0; 144 /* LCOV_EXCL_STOP */ 145 } 146 147 #if SIZE_MAX != UINT64_MAX 148 if (m > SIZE_MAX) { 149 /* I think this could happen if we're a 32-bit Tor running on a 64-bit 150 * system: we could have more system memory than would fit in a 151 * size_t. */ 152 m = SIZE_MAX; 153 } 154 #endif /* SIZE_MAX != UINT64_MAX */ 155 156 *mem_out = mem_cached = (size_t) m; 157 158 return 0; 159 }