Line data Source code
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 690 : 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 690 : unsigned long long result=0;
60 690 : int fd = -1;
61 690 : char *s = NULL;
62 690 : const char *cp;
63 690 : size_t file_size=0;
64 690 : if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
65 : return 0;
66 690 : s = read_file_to_str_until_eof(fd, 65536, &file_size);
67 690 : if (!s)
68 0 : goto err;
69 690 : cp = find_str_at_start_of_line(s, "MemTotal:");
70 690 : if (!cp)
71 0 : goto err;
72 : /* Use the system sscanf so that space will match a wider number of space */
73 690 : if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
74 0 : goto err;
75 :
76 690 : close(fd);
77 690 : tor_free(s);
78 690 : 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 690 : MOCK_IMPL(int,
129 : get_total_system_memory, (size_t *mem_out))
130 : {
131 690 : static size_t mem_cached=0;
132 690 : uint64_t m = get_total_system_memory_impl();
133 690 : 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 690 : *mem_out = mem_cached = (size_t) m;
157 :
158 690 : return 0;
159 : }
|