/src/tor/src/lib/meminfo/meminfo.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
56 | 0 | #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 | 0 | unsigned long long result=0; |
60 | 0 | int fd = -1; |
61 | 0 | char *s = NULL; |
62 | 0 | const char *cp; |
63 | 0 | size_t file_size=0; |
64 | 0 | if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0))) |
65 | 0 | return 0; |
66 | 0 | s = read_file_to_str_until_eof(fd, 65536, &file_size); |
67 | 0 | if (!s) |
68 | 0 | goto err; |
69 | 0 | cp = find_str_at_start_of_line(s, "MemTotal:"); |
70 | 0 | if (!cp) |
71 | 0 | goto err; |
72 | | /* Use the system sscanf so that space will match a wider number of space */ |
73 | 0 | if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1) |
74 | 0 | goto err; |
75 | | |
76 | 0 | close(fd); |
77 | 0 | tor_free(s); |
78 | 0 | return result * 1024; |
79 | | |
80 | | /* LCOV_EXCL_START Can't reach this unless proc is broken. */ |
81 | 0 | err: |
82 | 0 | tor_free(s); |
83 | 0 | close(fd); |
84 | 0 | 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 | 0 | } |
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 | 0 | { |
131 | 0 | static size_t mem_cached=0; |
132 | 0 | uint64_t m = get_total_system_memory_impl(); |
133 | 0 | if (0 == m) { |
134 | | /* LCOV_EXCL_START -- can't make this happen without mocking. */ |
135 | | /* We couldn't find our memory total */ |
136 | 0 | if (0 == mem_cached) { |
137 | | /* We have no cached value either */ |
138 | 0 | *mem_out = 0; |
139 | 0 | return -1; |
140 | 0 | } |
141 | | |
142 | 0 | *mem_out = mem_cached; |
143 | 0 | return 0; |
144 | | /* LCOV_EXCL_STOP */ |
145 | 0 | } |
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 | 0 | *mem_out = mem_cached = (size_t) m; |
157 | |
|
158 | 0 | return 0; |
159 | 0 | } |