changeset 14235:6b8b94f919ce

vma-iter, get-rusage-as: Add OpenBSD support. * modules/vma-iter (configure.ac): Test for mquery. * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on OpenBSD. * lib/vma-iter.c: Include <sys/mman.h>. (vma_iterate): Add an implementation based on mquery(). * lib/resource-ext.h (get_rusage_as): Update comments. * lib/get-rusage-as.c: Likewise. * lib/get-rusage-data.c: Likewise.
author Bruno Haible <bruno@clisp.org>
date Thu, 27 Jan 2011 11:42:45 +0100
parents 3a91ae6c2f46
children f0d7c8eb7ec3
files ChangeLog lib/get-rusage-as.c lib/get-rusage-data.c lib/resource-ext.h lib/vma-iter.c lib/vma-iter.h modules/vma-iter
diffstat 7 files changed, 116 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2011-01-27  Bruno Haible  <bruno@clisp.org>
+
+	vma-iter, get-rusage-as: Add OpenBSD support.
+	* modules/vma-iter (configure.ac): Test for mquery.
+	* lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on OpenBSD.
+	* lib/vma-iter.c: Include <sys/mman.h>.
+	(vma_iterate): Add an implementation based on mquery().
+	* lib/resource-ext.h (get_rusage_as): Update comments.
+	* lib/get-rusage-as.c: Likewise.
+	* lib/get-rusage-data.c: Likewise.
+
 2011-01-26  Karl Berry  <karl@gnu.org>
 
 	* doc/Makefile (lang_env, makeinfo_prog, manual_opts): new
--- a/lib/get-rusage-as.c
+++ b/lib/get-rusage-as.c
@@ -66,7 +66,7 @@
 
    OpenBSD:
      a) setrlimit exists, but RLIMIT_AS is not defined.
-     b) No VMA iteration API exists.
+     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
--- a/lib/get-rusage-data.c
+++ b/lib/get-rusage-data.c
@@ -69,7 +69,9 @@
 
    OpenBSD:
      a) setrlimit with RLIMIT_DATA works.
-     b) No VMA iteration API exists.
+     b) mquery() can be used to find out about the virtual memory areas.
+     get_rusage_data_via_setrlimit() works much better than
+     get_rusage_data_via_iterator().
      Note that malloc() appears to use mmap() for both large and small
      allocations.
 
--- a/lib/resource-ext.h
+++ b/lib/resource-ext.h
@@ -28,7 +28,7 @@
 /* Returns the amount of address space currently in use by the current
    process, or zero if unknown.
    This is the quantity which is limited by setrlimit(RLIMIT_AS,...).
-   Note: This function always returns zero on OpenBSD and AIX.  */
+   Note: This function always returns zero on AIX.  */
 extern uintptr_t get_rusage_as (void);
 
 /* Returns the size of the data segment, or zero if unknown.
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -44,6 +44,11 @@
 # include <OS.h>
 #endif
 
+#if HAVE_MQUERY /* OpenBSD */
+# include <sys/types.h>
+# include <sys/mman.h> /* mquery */
+#endif
+
 
 /* Support for reading text files in the /proc file system.  */
 
@@ -490,6 +495,99 @@
         break;
     }
 
+#elif HAVE_MQUERY /* OpenBSD */
+
+  uintptr_t pagesize;
+  uintptr_t address;
+  int /*bool*/ address_known_mapped;
+
+  pagesize = getpagesize ();
+  /* Avoid calling mquery with a NULL first argument, because this argument
+     value has a specific meaning.  We know the NULL page is unmapped.  */
+  address = pagesize;
+  address_known_mapped = 0;
+  for (;;)
+    {
+      /* Test whether the page at address is mapped.  */
+      if (address_known_mapped
+          || mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0)
+             == (void *) -1)
+        {
+          /* The page at address is mapped.
+             This is the start of an interval.  */
+          uintptr_t start = address;
+          uintptr_t end;
+
+          /* Find the end of the interval.  */
+          end = (uintptr_t) mquery ((void *) address, pagesize, 0, 0, -1, 0);
+          if (end == (uintptr_t) (void *) -1)
+            end = 0; /* wrap around */
+          address = end;
+
+          /* It's too complicated to find out about the flags.  Just pass 0.  */
+          if (callback (data, start, end, 0))
+            break;
+
+          if (address < pagesize) /* wrap around? */
+            break;
+        }
+      /* Here we know that the page at address is unmapped.  */
+      {
+        uintptr_t query_size = pagesize;
+
+        address += pagesize;
+
+        /* Query larger and larger blocks, to get through the unmapped address
+           range with few mquery() calls.  */
+        for (;;)
+          {
+            if (2 * query_size > query_size)
+              query_size = 2 * query_size;
+            if (address + query_size - 1 < query_size) /* wrap around? */
+              {
+                address_known_mapped = 0;
+                break;
+              }
+            if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
+                == (void *) -1)
+              {
+                /* Not all the interval [address .. address + query_size - 1]
+                   is unmapped.  */
+                address_known_mapped = (query_size == pagesize);
+                break;
+              }
+            /* The interval [address .. address + query_size - 1] is
+               unmapped.  */
+            address += query_size;
+          }
+        /* Reduce the query size again, to determine the precise size of the
+           unmapped interval that starts at address.  */
+        while (query_size > pagesize)
+          {
+            query_size = query_size / 2;
+            if (address + query_size - 1 >= query_size)
+              {
+                if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
+                    != (void *) -1)
+                  {
+                    /* The interval [address .. address + query_size - 1] is
+                       unmapped.  */
+                    address += query_size;
+                    address_known_mapped = 0;
+                  }
+                else
+                  address_known_mapped = (query_size == pagesize);
+              }
+          }
+        /* Here again query_size = pagesize, and
+           either address + pagesize - 1 < pagesize, or
+           mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0) fails.
+           So, the unmapped area ends at address.  */
+      }
+      if (address + pagesize - 1 < pagesize) /* wrap around? */
+        break;
+    }
+
 #endif
 }
 
--- a/lib/vma-iter.h
+++ b/lib/vma-iter.h
@@ -51,7 +51,7 @@
    this platform.
    Note that even when this macro is defined, vma_iterate() may still fail to
    find any virtual memory area, for example if /proc is not mounted.  */
-#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__
+#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY
 # define VMA_ITERATE_SUPPORTED 1
 #endif
 
--- a/modules/vma-iter
+++ b/modules/vma-iter
@@ -14,6 +14,7 @@
 
 configure.ac:
 gl_FUNC_MMAP_ANON
+AC_CHECK_FUNCS_ONCE([mquery])
 
 Makefile.am:
 lib_SOURCES += vma-iter.c