1 /* Getter for RLIMIT_AS.
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2011.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "resource-ext.h"
23 /* The "address space size" is defined as the total size of the virtual memory
24 areas of the current process. This includes
25 - areas belonging to the executable and shared libraries,
26 - areas allocated by malloc() or mmap(),
27 - the stack and environment areas,
28 - gaps and guard pages (mappings with PROT_NONE),
29 - other system dependent areas, such as vsyscall or vdso on Linux.
31 There are two ways of retrieving the current address space size:
32 a) by trying setrlimit with various values and observing whether the
33 kernel allows additional mmap calls,
34 b) by using system dependent APIs that allow to iterate over the list
35 of virtual memory areas.
36 We don't use the mincore() based approach here, because it would be very
37 slow when applied to an entire address space, especially on 64-bit
39 We define two functions
40 get_rusage_as_via_setrlimit(),
41 get_rusage_as_via_iterator().
43 Discussion per platform:
46 a) setrlimit with RLIMIT_AS works.
47 b) The /proc/self/maps file contains a list of the virtual memory areas.
48 Both methods agree, except that on x86_64 systems, the value of
49 get_rusage_as_via_iterator() is 4 KB higher than
50 get_rusage_as_via_setrlimit().
53 a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
54 ignores RLIMIT_AS. mmap() of a page always succeeds, therefore
55 get_rusage_as_via_setrlimit() is always 0.
56 b) The Mach based API works.
59 a) setrlimit with RLIMIT_AS works.
60 b) The /proc/self/maps file contains a list of the virtual memory areas.
63 a) setrlimit with RLIMIT_AS works.
64 b) The /proc/self/maps file contains a list of the virtual memory areas.
68 a) setrlimit exists, but RLIMIT_AS is not defined.
69 b) mquery() can be used to find out about the virtual memory areas.
72 a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
73 apparently ignores RLIMIT_AS. mmap() of a page always succeeds,
74 therefore get_rusage_as_via_setrlimit() is always 0.
75 b) No VMA iteration API exists.
78 a) setrlimit with RLIMIT_AS works.
79 b) No VMA iteration API exists.
82 a) setrlimit with RLIMIT_AS works.
83 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
87 a) setrlimit with RLIMIT_AS works.
88 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
89 The value returned by get_rusage_as_via_setrlimit() is 64 KB higher than
90 get_rusage_as_via_iterator(). It's not clear why.
93 a) setrlimit with RLIMIT_AS works.
94 b) No VMA iteration API exists.
97 a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000.
98 get_rusage_as_via_setrlimit() therefore produces a wrong value.
99 b) The /proc/$pid/maps file lists only the memory areas belonging to
100 the executable and shared libraries, not the anonymous memory.
101 But the native Win32 API works.
104 a) There is no setrlimit function.
105 b) The native Win32 API works.
108 a) On BeOS, there is no setrlimit function.
109 On Haiku, setrlimit exists. RLIMIT_AS is defined but unsupported.
110 b) There is a specific BeOS API: get_next_area_info().
114 #include <errno.h> /* errno */
115 #include <stdlib.h> /* size_t, abort */
116 #include <fcntl.h> /* open, O_RDONLY */
117 #include <unistd.h> /* getpagesize, read, close */
120 /* System support for get_rusage_as_via_setrlimit(). */
123 # include <sys/time.h>
124 # include <sys/resource.h> /* getrlimit, setrlimit */
127 /* Test whether mmap() and mprotect() are available.
128 We don't use HAVE_MMAP, because AC_FUNC_MMAP would not define it on HP-UX.
129 HAVE_MPROTECT is not enough, because mingw does not have mmap() but has an
130 mprotect() function in libgcc.a. */
131 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
133 # include <sys/types.h>
134 # include <sys/mman.h> /* mmap, munmap */
135 /* Define MAP_FILE when it isn't otherwise. */
142 /* System support for get_rusage_as_via_iterator(). */
144 #include "vma-iter.h"
147 #if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
149 static inline uintptr_t
150 get_rusage_as_via_setrlimit (void)
154 struct rlimit orig_limit;
156 # if HAVE_MAP_ANONYMOUS
157 const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
159 # else /* !HAVE_MAP_ANONYMOUS */
160 const int flags = MAP_FILE | MAP_PRIVATE;
161 int fd = open ("/dev/zero", O_RDONLY, 0666);
166 /* Record the original limit. */
167 if (getrlimit (RLIMIT_AS, &orig_limit) < 0)
173 if (orig_limit.rlim_max != RLIM_INFINITY
174 && (orig_limit.rlim_cur == RLIM_INFINITY
175 || orig_limit.rlim_cur > orig_limit.rlim_max))
176 /* We may not be able to restore the current rlim_cur value.
184 /* The granularity is a single page. */
185 const size_t pagesize = getpagesize ();
187 uintptr_t low_bound = 0;
188 uintptr_t high_bound;
192 /* Here we know that the address space size is >= low_bound. */
193 struct rlimit try_limit;
194 uintptr_t try_next = 2 * low_bound + pagesize;
196 if (try_next < low_bound)
198 try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize;
200 /* There's no point in trying a value > orig_limit.rlim_max, as
201 setrlimit would fail anyway. */
202 if (orig_limit.rlim_max != RLIM_INFINITY
203 && orig_limit.rlim_max < try_next)
204 try_next = orig_limit.rlim_max;
206 /* Avoid endless loop. */
207 if (try_next == low_bound)
209 /* try_next could not be increased. */
214 try_limit.rlim_max = orig_limit.rlim_max;
215 try_limit.rlim_cur = try_next;
216 if (setrlimit (RLIMIT_AS, &try_limit) == 0)
218 /* Allocate a page of memory, to compare the current address space
219 size with try_limit.rlim_cur. */
221 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
223 if (new_page != (void *)(-1))
225 /* The page could be added successfully. Free it. */
226 if (munmap (new_page, pagesize) < 0)
228 /* We know that the address space size is
229 < try_limit.rlim_cur. */
230 high_bound = try_next;
235 /* We know that the address space size is
236 >= try_limit.rlim_cur. */
237 low_bound = try_next;
242 /* Here we expect only EINVAL, not EPERM. */
245 /* We know that the address space size is
246 >= try_limit.rlim_cur. */
247 low_bound = try_next;
251 /* Here we know that the address space size is
252 >= low_bound and < high_bound. */
253 while (high_bound - low_bound > pagesize)
255 struct rlimit try_limit;
257 low_bound + (((high_bound - low_bound) / 2) / pagesize) * pagesize;
259 /* Here low_bound <= try_next < high_bound. */
260 try_limit.rlim_max = orig_limit.rlim_max;
261 try_limit.rlim_cur = try_next;
262 if (setrlimit (RLIMIT_AS, &try_limit) == 0)
264 /* Allocate a page of memory, to compare the current address space
265 size with try_limit.rlim_cur. */
267 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
269 if (new_page != (void *)(-1))
271 /* The page could be added successfully. Free it. */
272 if (munmap (new_page, pagesize) < 0)
274 /* We know that the address space size is
275 < try_limit.rlim_cur. */
276 high_bound = try_next;
280 /* We know that the address space size is
281 >= try_limit.rlim_cur. */
282 low_bound = try_next;
287 /* Here we expect only EINVAL, not EPERM. */
290 /* We know that the address space size is
291 >= try_limit.rlim_cur. */
292 low_bound = try_next;
300 /* Restore the original rlim_cur value. */
301 if (setrlimit (RLIMIT_AS, &orig_limit) < 0)
305 # if !HAVE_MAP_ANONYMOUS
313 static inline uintptr_t
314 get_rusage_as_via_setrlimit (void)
322 #if VMA_ITERATE_SUPPORTED
325 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
328 uintptr_t *totalp = (uintptr_t *) data;
330 *totalp += end - start;
334 static inline uintptr_t
335 get_rusage_as_via_iterator (void)
339 vma_iterate (vma_iterate_callback, &total);
346 static inline uintptr_t
347 get_rusage_as_via_iterator (void)
358 #if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* MacOS X, AIX, Cygwin */
359 /* get_rusage_as_via_setrlimit() does not work.
360 Prefer get_rusage_as_via_iterator(). */
361 return get_rusage_as_via_iterator ();
362 #elif HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
363 /* Prefer get_rusage_as_via_setrlimit() if it succeeds,
364 because the caller may want to use the result with setrlimit(). */
367 result = get_rusage_as_via_setrlimit ();
369 result = get_rusage_as_via_iterator ();
372 return get_rusage_as_via_iterator ();
384 printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n",
385 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
388 printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n",
389 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
392 printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n",
393 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
396 printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n",
397 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),