1 /* Getter for RLIMIT_DATA.
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 "data segment size" is defined as the virtual memory area of the
24 current process that contains malloc()ed memory.
26 There are two ways of retrieving the current data segment size:
27 a) by trying setrlimit with various values and observing whether the
28 kernel allows additional sbrk() calls,
29 b) by using system dependent APIs that allow to iterate over the list
30 of virtual memory areas.
31 We define two functions
32 get_rusage_data_via_setrlimit(),
33 get_rusage_data_via_iterator().
36 a') by trying setrlimit with various values and observing whether
37 additional malloc() calls succeed
38 is not as good as a), because a malloc() call can be served by already
39 allocated memory or through mmap(), and because a malloc() of 1 page may
42 Discussion per platform:
45 a) setrlimit with RLIMIT_DATA works.
46 b) The /proc/self/maps file contains a list of the virtual memory areas.
47 get_rusage_data_via_setrlimit() returns the sum of the length of the
48 executable's data segment plus the heap VMA (an anonymous memory area),
49 whereas get_rusage_data_via_iterator() returns only the latter.
50 Note that malloc() falls back on mmap() for large allocations and also
51 for small allocations if there is not enough room in the data segment.
54 a) setrlimit with RLIMIT_DATA succeeds but does not really work: The OS
55 ignores RLIMIT_DATA. Therefore get_rusage_data_via_setrlimit() is
57 b) The Mach based API works.
58 Note that malloc() falls back on mmap() for large allocations.
61 a) setrlimit with RLIMIT_DATA works.
62 b) The /proc/self/maps file contains a list of the virtual memory areas.
65 a) setrlimit with RLIMIT_DATA works.
66 b) The /proc/self/maps file contains a list of the virtual memory areas.
68 Note that malloc() uses mmap() for large allocations.
71 a) setrlimit with RLIMIT_DATA works.
72 b) No VMA iteration API exists.
73 Note that malloc() appears to use mmap() for both large and small
77 a) setrlimit with RLIMIT_DATA works.
78 b) No VMA iteration API exists.
81 a) setrlimit with RLIMIT_DATA works, except on HP-UX 11.00, where it
82 cannot restore the previous limits, and except on HP-UX 11.11, where
83 it sometimes has no effect.
84 b) No VMA iteration API exists.
87 a) setrlimit with RLIMIT_DATA works.
88 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
89 get_rusage_data_via_setrlimit() works slightly better than
90 get_rusage_data_via_iterator() before the first malloc() call.
93 a) setrlimit with RLIMIT_DATA works.
94 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
98 a) setrlimit with RLIMIT_DATA works.
99 b) No VMA iteration API exists.
102 a) setrlimit with RLIMIT_DATA always fails.
103 get_rusage_data_via_setrlimit() therefore produces a wrong value.
104 b) The /proc/$pid/maps file lists only the memory areas belonging to
105 the executable and shared libraries, not the anonymous memory.
106 But the native Win32 API works.
107 Note that malloc() apparently falls back on mmap() for large allocations.
110 a) There is no setrlimit function.
111 b) There is no sbrk() function.
112 Note that malloc() falls back on VirtualAlloc() for large allocations.
115 a) On BeOS, there is no setrlimit function.
116 On Haiku, setrlimit exists. RLIMIT_DATA is defined but unsupported:
117 getrlimit of RLIMIT_DATA always fails with errno = EINVAL.
118 b) There is a specific BeOS API: get_next_area_info().
122 #include <errno.h> /* errno */
123 #include <stdlib.h> /* size_t, abort, malloc, free, sbrk */
124 #include <fcntl.h> /* open, O_RDONLY */
125 #include <unistd.h> /* getpagesize, read, close */
128 /* System support for get_rusage_data_via_setrlimit(). */
131 # include <sys/time.h>
132 # include <sys/resource.h> /* getrlimit, setrlimit */
133 # include <sys/utsname.h>
134 # include <string.h> /* strlen, strcmp */
138 /* System support for get_rusage_data_via_iterator(). */
140 #include "vma-iter.h"
143 #if HAVE_SETRLIMIT && defined RLIMIT_DATA
146 # define errno_expected() (errno == EINVAL || errno == EFAULT)
148 # define errno_expected() (errno == EINVAL)
151 static inline uintptr_t
152 get_rusage_data_via_setrlimit (void)
156 struct rlimit orig_limit;
159 /* On HP-UX 11.00, setrlimit() RLIMIT_DATA of does not work: It cannot
160 restore the previous limits.
161 On HP-UX 11.11, setrlimit() RLIMIT_DATA of does not work: It sometimes
162 has no effect on the next sbrk() call. */
166 if (uname (&buf) == 0
167 && strlen (buf.release) >= 5
168 && (strcmp (buf.release + strlen (buf.release) - 5, "11.00") == 0
169 || strcmp (buf.release + strlen (buf.release) - 5, "11.11") == 0))
174 /* Record the original limit. */
175 if (getrlimit (RLIMIT_DATA, &orig_limit) < 0)
178 if (orig_limit.rlim_max != RLIM_INFINITY
179 && (orig_limit.rlim_cur == RLIM_INFINITY
180 || orig_limit.rlim_cur > orig_limit.rlim_max))
181 /* We may not be able to restore the current rlim_cur value.
186 /* The granularity is a single page. */
187 const intptr_t pagesize = getpagesize ();
189 uintptr_t low_bound = 0;
190 uintptr_t high_bound;
194 /* Here we know that the data segment size is >= low_bound. */
195 struct rlimit try_limit;
196 uintptr_t try_next = 2 * low_bound + pagesize;
198 if (try_next < low_bound)
200 try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize;
202 /* There's no point in trying a value > orig_limit.rlim_max, as
203 setrlimit would fail anyway. */
204 if (orig_limit.rlim_max != RLIM_INFINITY
205 && orig_limit.rlim_max < try_next)
206 try_next = orig_limit.rlim_max;
208 /* Avoid endless loop. */
209 if (try_next == low_bound)
211 /* try_next could not be increased. */
216 try_limit.rlim_max = orig_limit.rlim_max;
217 try_limit.rlim_cur = try_next;
218 if (setrlimit (RLIMIT_DATA, &try_limit) == 0)
220 /* Allocate a page of memory, to compare the current data segment
221 size with try_limit.rlim_cur. */
222 void *new_page = sbrk (pagesize);
224 if (new_page != (void *)(-1))
226 /* The page could be added successfully. Free it. */
228 /* We know that the data segment size is
229 < try_limit.rlim_cur. */
230 high_bound = try_next;
235 /* We know that the data segment size is
236 >= try_limit.rlim_cur. */
237 low_bound = try_next;
242 /* Here we expect only EINVAL or (on AIX) EFAULT, not EPERM. */
243 if (! errno_expected ())
245 /* We know that the data segment size is
246 >= try_limit.rlim_cur. */
247 low_bound = try_next;
251 /* Here we know that the data segment 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_DATA, &try_limit) == 0)
264 /* Allocate a page of memory, to compare the current data segment
265 size with try_limit.rlim_cur. */
266 void *new_page = sbrk (pagesize);
268 if (new_page != (void *)(-1))
270 /* The page could be added successfully. Free it. */
272 /* We know that the data segment size is
273 < try_limit.rlim_cur. */
274 high_bound = try_next;
278 /* We know that the data segment size is
279 >= try_limit.rlim_cur. */
280 low_bound = try_next;
285 /* Here we expect only EINVAL or (on AIX) EFAULT, not EPERM. */
286 if (! errno_expected ())
288 /* We know that the data segment size is
289 >= try_limit.rlim_cur. */
290 low_bound = try_next;
298 /* Restore the original rlim_cur value. */
299 if (setrlimit (RLIMIT_DATA, &orig_limit) < 0)
307 static inline uintptr_t
308 get_rusage_data_via_setrlimit (void)
316 #if VMA_ITERATE_SUPPORTED
321 uintptr_t data_segment_size;
325 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
328 struct locals *lp = (struct locals *) data;
330 if (start <= lp->brk_value && lp->brk_value - 1 <= end - 1)
332 lp->data_segment_size = end - start;
338 static inline uintptr_t
339 get_rusage_data_via_iterator (void)
341 # if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __BEOS__ || defined __HAIKU__
342 /* On mingw, there is no sbrk() function.
343 On Haiku, sbrk(0) always returns 0. */
344 static void *brk_value;
346 if (brk_value == NULL)
348 brk_value = malloc (1);
349 if (brk_value == NULL)
355 brk_value = sbrk (0);
356 if (brk_value == (void *)-1)
363 l.brk_value = (uintptr_t) brk_value;
364 l.data_segment_size = 0;
365 vma_iterate (vma_iterate_callback, &l);
367 return l.data_segment_size;
373 static inline uintptr_t
374 get_rusage_data_via_iterator (void)
383 get_rusage_data (void)
385 #if (defined __APPLE__ && defined __MACH__) || defined __CYGWIN__ /* MacOS X, Cygwin */
386 /* get_rusage_data_via_setrlimit() does not work.
387 Prefer get_rusage_data_via_iterator(). */
388 return get_rusage_data_via_iterator ();
389 #elif HAVE_SETRLIMIT && defined RLIMIT_DATA
390 # if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined _AIX || defined __sgi || defined __osf__ || defined __sun /* Linux, FreeBSD, NetBSD, OpenBSD, AIX, IRIX, OSF/1, Solaris */
391 /* get_rusage_data_via_setrlimit() works. */
392 return get_rusage_data_via_setrlimit ();
394 /* Prefer get_rusage_data_via_setrlimit() if it succeeds,
395 because the caller may want to use the result with setrlimit(). */
398 result = get_rusage_data_via_setrlimit ();
400 result = get_rusage_data_via_iterator ();
404 return get_rusage_data_via_iterator ();
416 printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n",
417 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (),
420 printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n",
421 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (),
424 printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n",
425 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (),
428 printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n",
429 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (),