summaryrefslogtreecommitdiff
path: root/gnulib/lib/get-rusage-as.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib/lib/get-rusage-as.c')
m---------gnulib0
-rw-r--r--gnulib/lib/get-rusage-as.c402
2 files changed, 402 insertions, 0 deletions
diff --git a/gnulib b/gnulib
deleted file mode 160000
-Subproject 443bc5ffcf7429e557f4a371b0661abe98ddbc1
diff --git a/gnulib/lib/get-rusage-as.c b/gnulib/lib/get-rusage-as.c
new file mode 100644
index 0000000..5567f95
--- /dev/null
+++ b/gnulib/lib/get-rusage-as.c
@@ -0,0 +1,402 @@
+/* Getter for RLIMIT_AS.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2011.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "resource-ext.h"
+
+/* The "address space size" is defined as the total size of the virtual memory
+ areas of the current process. This includes
+ - areas belonging to the executable and shared libraries,
+ - areas allocated by malloc() or mmap(),
+ - the stack and environment areas,
+ - gaps and guard pages (mappings with PROT_NONE),
+ - other system dependent areas, such as vsyscall or vdso on Linux.
+
+ There are two ways of retrieving the current address space size:
+ a) by trying setrlimit with various values and observing whether the
+ kernel allows additional mmap calls,
+ b) by using system dependent APIs that allow to iterate over the list
+ of virtual memory areas.
+ We don't use the mincore() based approach here, because it would be very
+ slow when applied to an entire address space, especially on 64-bit
+ platforms.
+ We define two functions
+ get_rusage_as_via_setrlimit(),
+ get_rusage_as_via_iterator().
+
+ Discussion per platform:
+
+ Linux:
+ a) setrlimit with RLIMIT_AS works.
+ b) The /proc/self/maps file contains a list of the virtual memory areas.
+ Both methods agree, except that on x86_64 systems, the value of
+ get_rusage_as_via_iterator() is 4 KB higher than
+ get_rusage_as_via_setrlimit().
+
+ MacOS X:
+ a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
+ ignores RLIMIT_AS. mmap() of a page always succeeds, therefore
+ get_rusage_as_via_setrlimit() is always 0.
+ b) The Mach based API works.
+
+ FreeBSD:
+ a) setrlimit with RLIMIT_AS works.
+ b) The /proc/self/maps file contains a list of the virtual memory areas.
+
+ NetBSD:
+ a) setrlimit with RLIMIT_AS works.
+ b) The /proc/self/maps file contains a list of the virtual memory areas.
+ Both methods agree,
+
+ OpenBSD:
+ a) setrlimit exists, but RLIMIT_AS is not defined.
+ b) mquery() can be used to find out about the virtual memory areas.
+
+ AIX:
+ a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
+ apparently ignores RLIMIT_AS. mmap() of a page always succeeds,
+ therefore get_rusage_as_via_setrlimit() is always 0.
+ b) No VMA iteration API exists.
+
+ HP-UX:
+ a) setrlimit with RLIMIT_AS works.
+ b) No VMA iteration API exists.
+
+ IRIX:
+ a) setrlimit with RLIMIT_AS works.
+ b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
+ Both methods agree,
+
+ OSF/1:
+ a) setrlimit with RLIMIT_AS works.
+ b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
+ The value returned by get_rusage_as_via_setrlimit() is 64 KB higher than
+ get_rusage_as_via_iterator(). It's not clear why.
+
+ Solaris:
+ a) setrlimit with RLIMIT_AS works.
+ b) No VMA iteration API exists.
+
+ Cygwin:
+ a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000.
+ get_rusage_as_via_setrlimit() therefore produces a wrong value.
+ b) The /proc/$pid/maps file lists only the memory areas belonging to
+ the executable and shared libraries, not the anonymous memory.
+ But the native Win32 API works.
+
+ mingw:
+ a) There is no setrlimit function.
+ b) The native Win32 API works.
+
+ BeOS, Haiku:
+ a) On BeOS, there is no setrlimit function.
+ On Haiku, setrlimit exists. RLIMIT_AS is defined but unsupported.
+ b) There is a specific BeOS API: get_next_area_info().
+ */
+
+
+#include <errno.h> /* errno */
+#include <stdlib.h> /* size_t, abort */
+#include <fcntl.h> /* open, O_RDONLY */
+#include <unistd.h> /* getpagesize, read, close */
+
+
+/* System support for get_rusage_as_via_setrlimit(). */
+
+#if HAVE_SETRLIMIT
+# include <sys/time.h>
+# include <sys/resource.h> /* getrlimit, setrlimit */
+#endif
+
+/* Test whether mmap() and mprotect() are available.
+ We don't use HAVE_MMAP, because AC_FUNC_MMAP would not define it on HP-UX.
+ HAVE_MPROTECT is not enough, because mingw does not have mmap() but has an
+ mprotect() function in libgcc.a. */
+#if HAVE_SYS_MMAN_H && HAVE_MPROTECT
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/mman.h> /* mmap, munmap */
+/* Define MAP_FILE when it isn't otherwise. */
+# ifndef MAP_FILE
+# define MAP_FILE 0
+# endif
+#endif
+
+
+/* System support for get_rusage_as_via_iterator(). */
+
+#include "vma-iter.h"
+
+
+#if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
+
+static inline uintptr_t
+get_rusage_as_via_setrlimit (void)
+{
+ uintptr_t result;
+
+ struct rlimit orig_limit;
+
+# if HAVE_MAP_ANONYMOUS
+ const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ const int fd = -1;
+# else /* !HAVE_MAP_ANONYMOUS */
+ const int flags = MAP_FILE | MAP_PRIVATE;
+ int fd = open ("/dev/zero", O_RDONLY, 0666);
+ if (fd < 0)
+ return 0;
+# endif
+
+ /* Record the original limit. */
+ if (getrlimit (RLIMIT_AS, &orig_limit) < 0)
+ {
+ result = 0;
+ goto done2;
+ }
+
+ if (orig_limit.rlim_max != RLIM_INFINITY
+ && (orig_limit.rlim_cur == RLIM_INFINITY
+ || orig_limit.rlim_cur > orig_limit.rlim_max))
+ /* We may not be able to restore the current rlim_cur value.
+ So bail out. */
+ {
+ result = 0;
+ goto done2;
+ }
+
+ {
+ /* The granularity is a single page. */
+ const size_t pagesize = getpagesize ();
+
+ uintptr_t low_bound = 0;
+ uintptr_t high_bound;
+
+ for (;;)
+ {
+ /* Here we know that the address space size is >= low_bound. */
+ struct rlimit try_limit;
+ uintptr_t try_next = 2 * low_bound + pagesize;
+
+ if (try_next < low_bound)
+ /* Overflow. */
+ try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize;
+
+ /* There's no point in trying a value > orig_limit.rlim_max, as
+ setrlimit would fail anyway. */
+ if (orig_limit.rlim_max != RLIM_INFINITY
+ && orig_limit.rlim_max < try_next)
+ try_next = orig_limit.rlim_max;
+
+ /* Avoid endless loop. */
+ if (try_next == low_bound)
+ {
+ /* try_next could not be increased. */
+ result = low_bound;
+ goto done1;
+ }
+
+ try_limit.rlim_max = orig_limit.rlim_max;
+ try_limit.rlim_cur = try_next;
+ if (setrlimit (RLIMIT_AS, &try_limit) == 0)
+ {
+ /* Allocate a page of memory, to compare the current address space
+ size with try_limit.rlim_cur. */
+ void *new_page =
+ mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
+
+ if (new_page != (void *)(-1))
+ {
+ /* The page could be added successfully. Free it. */
+ if (munmap (new_page, pagesize) < 0)
+ abort ();
+ /* We know that the address space size is
+ < try_limit.rlim_cur. */
+ high_bound = try_next;
+ break;
+ }
+ else
+ {
+ /* We know that the address space size is
+ >= try_limit.rlim_cur. */
+ low_bound = try_next;
+ }
+ }
+ else
+ {
+ /* Here we expect only EINVAL, not EPERM. */
+ if (errno != EINVAL)
+ abort ();
+ /* We know that the address space size is
+ >= try_limit.rlim_cur. */
+ low_bound = try_next;
+ }
+ }
+
+ /* Here we know that the address space size is
+ >= low_bound and < high_bound. */
+ while (high_bound - low_bound > pagesize)
+ {
+ struct rlimit try_limit;
+ uintptr_t try_next =
+ low_bound + (((high_bound - low_bound) / 2) / pagesize) * pagesize;
+
+ /* Here low_bound <= try_next < high_bound. */
+ try_limit.rlim_max = orig_limit.rlim_max;
+ try_limit.rlim_cur = try_next;
+ if (setrlimit (RLIMIT_AS, &try_limit) == 0)
+ {
+ /* Allocate a page of memory, to compare the current address space
+ size with try_limit.rlim_cur. */
+ void *new_page =
+ mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
+
+ if (new_page != (void *)(-1))
+ {
+ /* The page could be added successfully. Free it. */
+ if (munmap (new_page, pagesize) < 0)
+ abort ();
+ /* We know that the address space size is
+ < try_limit.rlim_cur. */
+ high_bound = try_next;
+ }
+ else
+ {
+ /* We know that the address space size is
+ >= try_limit.rlim_cur. */
+ low_bound = try_next;
+ }
+ }
+ else
+ {
+ /* Here we expect only EINVAL, not EPERM. */
+ if (errno != EINVAL)
+ abort ();
+ /* We know that the address space size is
+ >= try_limit.rlim_cur. */
+ low_bound = try_next;
+ }
+ }
+
+ result = low_bound;
+ }
+
+ done1:
+ /* Restore the original rlim_cur value. */
+ if (setrlimit (RLIMIT_AS, &orig_limit) < 0)
+ abort ();
+
+ done2:
+# if !HAVE_MAP_ANONYMOUS
+ close (fd);
+# endif
+ return result;
+}
+
+#else
+
+static inline uintptr_t
+get_rusage_as_via_setrlimit (void)
+{
+ return 0;
+}
+
+#endif
+
+
+#if VMA_ITERATE_SUPPORTED
+
+static int
+vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
+ unsigned int flags)
+{
+ uintptr_t *totalp = (uintptr_t *) data;
+
+ *totalp += end - start;
+ return 0;
+}
+
+static inline uintptr_t
+get_rusage_as_via_iterator (void)
+{
+ uintptr_t total = 0;
+
+ vma_iterate (vma_iterate_callback, &total);
+
+ return total;
+}
+
+#else
+
+static inline uintptr_t
+get_rusage_as_via_iterator (void)
+{
+ return 0;
+}
+
+#endif
+
+
+uintptr_t
+get_rusage_as (void)
+{
+#if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* MacOS X, AIX, Cygwin */
+ /* get_rusage_as_via_setrlimit() does not work.
+ Prefer get_rusage_as_via_iterator(). */
+ return get_rusage_as_via_iterator ();
+#elif HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
+ /* Prefer get_rusage_as_via_setrlimit() if it succeeds,
+ because the caller may want to use the result with setrlimit(). */
+ uintptr_t result;
+
+ result = get_rusage_as_via_setrlimit ();
+ if (result == 0)
+ result = get_rusage_as_via_iterator ();
+ return result;
+#else
+ return get_rusage_as_via_iterator ();
+#endif
+}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main ()
+{
+ printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x88);
+ printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x8812);
+ printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x281237);
+ printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ return 0;
+}
+
+#endif /* TEST */