changeset 18748:6ec64ad1fe3c

vma-iter: Add support for Solaris. * lib/vma-iter.c (vma_iterate): On Solaris, use the /proc filesystem approach. * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on Solaris. * lib/get-rusage-as.c: Update comment about Solaris. * lib/get-rusage-data.c: Likewise.
author Bruno Haible <bruno@clisp.org>
date Sun, 19 Mar 2017 15:45:26 +0100
parents d780e32c9771
children 93f3ea7943ba
files ChangeLog lib/get-rusage-as.c lib/get-rusage-data.c lib/vma-iter.c lib/vma-iter.h
diffstat 5 files changed, 269 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Mar 19 14:19:07 2017 +0100
+++ b/ChangeLog	Sun Mar 19 15:45:26 2017 +0100
@@ -1,3 +1,12 @@
+2017-03-19  Bruno Haible  <bruno@clisp.org>
+
+	vma-iter: Add support for Solaris.
+	* lib/vma-iter.c (vma_iterate): On Solaris, use the /proc filesystem
+	approach.
+	* lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on Solaris.
+	* lib/get-rusage-as.c: Update comment about Solaris.
+	* lib/get-rusage-data.c: Likewise.
+
 2017-03-19  Bruno Haible  <bruno@clisp.org>
 
 	vma-iter: Prefer HP-UX specific API on HP-UX.
--- a/lib/get-rusage-as.c	Sun Mar 19 14:19:07 2017 +0100
+++ b/lib/get-rusage-as.c	Sun Mar 19 15:45:26 2017 +0100
@@ -95,7 +95,9 @@
 
    Solaris:
      a) setrlimit with RLIMIT_AS works.
-     b) No VMA iteration API exists.
+     b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP, and the
+        /proc/self/maps file contains a list of the virtual memory areas.
+     Both methods agree,
 
    Cygwin:
      a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000.
--- a/lib/get-rusage-data.c	Sun Mar 19 14:19:07 2017 +0100
+++ b/lib/get-rusage-data.c	Sun Mar 19 15:45:26 2017 +0100
@@ -101,7 +101,10 @@
 
    Solaris:
      a) setrlimit with RLIMIT_DATA works.
-     b) No VMA iteration API exists.
+     b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP, and the
+        /proc/self/maps file contains a list of the virtual memory areas.
+     get_rusage_data_via_setrlimit() ignores the data segment of the executable,
+     whereas get_rusage_data_via_iterator() includes it.
 
    Cygwin:
      a) setrlimit with RLIMIT_DATA always fails.
--- a/lib/vma-iter.c	Sun Mar 19 14:19:07 2017 +0100
+++ b/lib/vma-iter.c	Sun Mar 19 15:45:26 2017 +0100
@@ -23,7 +23,7 @@
 #include <errno.h> /* errno */
 #include <stdlib.h> /* size_t */
 #include <fcntl.h> /* open, O_RDONLY */
-#include <unistd.h> /* getpagesize, read, close */
+#include <unistd.h> /* getpagesize, read, close, getpid */
 
 #if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
 # include <string.h> /* memcpy */
@@ -32,6 +32,15 @@
 # include <sys/procfs.h> /* PIOC*, prmap_t */
 #endif
 
+#if defined __sun /* Solaris */
+# include <string.h> /* memcpy */
+# include <sys/types.h>
+# include <sys/mman.h> /* mmap, munmap */
+/* Try to use the newer ("structured") /proc filesystem API, if supported.  */
+# define _STRUCTURED_PROC 1
+# include <sys/procfs.h> /* prmap_t, optionally PIOC* */
+#endif
+
 #if HAVE_PSTAT_GETPROCVM /* HP-UX */
 # include <sys/pstat.h> /* pstat_getprocvm */
 #endif
@@ -369,6 +378,248 @@
   close (fd);
   return -1;
 
+#elif defined __sun /* Solaris */
+
+  /* Note: Solaris <sys/procfs.h> defines a different type prmap_t with
+     _STRUCTURED_PROC than without! Here's a table of sizeof(prmap_t):
+                                  32-bit   64-bit
+         _STRUCTURED_PROC = 0       32       56
+         _STRUCTURED_PROC = 1       96      104
+     Therefore, if the include files provide the newer API, prmap_t has
+     the bigger size, and thus you MUST use the newer API.  And if the
+     include files provide the older API, prmap_t has the smaller size,
+     and thus you MUST use the older API.  */
+
+# if defined PIOCNMAP && defined PIOCMAP
+  /* We must use the older /proc interface.  */
+
+  size_t pagesize;
+  char fnamebuf[6+10+1];
+  char *fname;
+  int fd;
+  int nmaps;
+  size_t memneed;
+  void *auxmap;
+  unsigned long auxmap_start;
+  unsigned long auxmap_end;
+  prmap_t* maps;
+  prmap_t* mp;
+
+  pagesize = getpagesize ();
+
+  /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()).  */
+  fname = fnamebuf + sizeof (fnamebuf) - 1;
+  *fname = '\0';
+  {
+    unsigned int value = getpid ();
+    do
+      *--fname = (value % 10) + '0';
+    while ((value = value / 10) > 0);
+  }
+  fname -= 6;
+  memcpy (fname, "/proc/", 6);
+
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    return -1;
+
+  if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
+    goto fail2;
+
+  memneed = (nmaps + 10) * sizeof (prmap_t);
+  /* Allocate memneed bytes of memory.
+     We cannot use alloca here, because not much stack space is guaranteed.
+     We also cannot use malloc here, because a malloc() call may call mmap()
+     and thus pre-allocate available memory.
+     So use mmap(), and ignore the resulting VMA.  */
+  memneed = ((memneed - 1) / pagesize + 1) * pagesize;
+  auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
+                          MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (auxmap == (void *) -1)
+    goto fail2;
+  auxmap_start = (unsigned long) auxmap;
+  auxmap_end = auxmap_start + memneed;
+  maps = (prmap_t *) auxmap;
+
+  if (ioctl (fd, PIOCMAP, maps) < 0)
+    goto fail1;
+
+  for (mp = maps;;)
+    {
+      unsigned long start, end;
+      unsigned int flags;
+
+      start = (unsigned long) mp->pr_vaddr;
+      end = start + mp->pr_size;
+      if (start == 0 && end == 0)
+        break;
+      flags = 0;
+      if (mp->pr_mflags & MA_READ)
+        flags |= VMA_PROT_READ;
+      if (mp->pr_mflags & MA_WRITE)
+        flags |= VMA_PROT_WRITE;
+      if (mp->pr_mflags & MA_EXEC)
+        flags |= VMA_PROT_EXECUTE;
+      mp++;
+      if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
+        {
+          /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
+             = [start,auxmap_start-1] u [auxmap_end,end-1].  */
+          if (start < auxmap_start)
+            if (callback (data, start, auxmap_start, flags))
+              break;
+          if (auxmap_end - 1 < end - 1)
+            if (callback (data, auxmap_end, end, flags))
+              break;
+        }
+      else
+        {
+          if (callback (data, start, end, flags))
+            break;
+        }
+    }
+  munmap (auxmap, memneed);
+  close (fd);
+  return 0;
+
+ fail1:
+  munmap (auxmap, memneed);
+ fail2:
+  close (fd);
+  return -1;
+
+# else
+  /* We must use the newer /proc interface.
+     Documentation:
+     https://docs.oracle.com/cd/E23824_01/html/821-1473/proc-4.html
+     The contents of /proc/<pid>/map consists of records of type
+     prmap_t.  These are different in 32-bit and 64-bit processes,
+     but here we are fortunately accessing only the current process.  */
+
+  size_t pagesize;
+  char fnamebuf[6+10+4+1];
+  char *fname;
+  int fd;
+  int nmaps;
+  size_t memneed;
+  void *auxmap;
+  unsigned long auxmap_start;
+  unsigned long auxmap_end;
+  prmap_t* maps;
+  prmap_t* maps_end;
+  prmap_t* mp;
+
+  pagesize = getpagesize ();
+
+  /* Construct fname = sprintf (fnamebuf+i, "/proc/%u/map", getpid ()).  */
+  fname = fnamebuf + sizeof (fnamebuf) - 1 - 4;
+  memcpy (fname, "/map", 4 + 1);
+  {
+    unsigned int value = getpid ();
+    do
+      *--fname = (value % 10) + '0';
+    while ((value = value / 10) > 0);
+  }
+  fname -= 6;
+  memcpy (fname, "/proc/", 6);
+
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    return -1;
+
+  {
+    struct stat statbuf;
+    if (fstat (fd, &statbuf) < 0)
+      goto fail2;
+    nmaps = statbuf.st_size / sizeof (prmap_t);
+  }
+
+  memneed = (nmaps + 10) * sizeof (prmap_t);
+  /* Allocate memneed bytes of memory.
+     We cannot use alloca here, because not much stack space is guaranteed.
+     We also cannot use malloc here, because a malloc() call may call mmap()
+     and thus pre-allocate available memory.
+     So use mmap(), and ignore the resulting VMA.  */
+  memneed = ((memneed - 1) / pagesize + 1) * pagesize;
+  auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
+                          MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (auxmap == (void *) -1)
+    goto fail2;
+  auxmap_start = (unsigned long) auxmap;
+  auxmap_end = auxmap_start + memneed;
+  maps = (prmap_t *) auxmap;
+
+  /* Read up to memneed bytes from fd into maps.  */
+  {
+    size_t remaining = memneed;
+    size_t total_read = 0;
+    char *ptr = (char *) maps;
+
+    do
+      {
+        size_t nread = read (fd, ptr, remaining);
+        if (nread == (size_t)-1)
+          {
+            if (errno == EINTR)
+              continue;
+            goto fail1;
+          }
+        if (nread == 0)
+          /* EOF */
+          break;
+        total_read += nread;
+        ptr += nread;
+        remaining -= nread;
+      }
+    while (remaining > 0);
+
+    nmaps = (memneed - remaining) / sizeof (prmap_t);
+    maps_end = maps + nmaps;
+  }
+
+  for (mp = maps; mp < maps_end; mp++)
+    {
+      unsigned long start, end;
+      unsigned int flags;
+
+      start = (unsigned long) mp->pr_vaddr;
+      end = start + mp->pr_size;
+      flags = 0;
+      if (mp->pr_mflags & MA_READ)
+        flags |= VMA_PROT_READ;
+      if (mp->pr_mflags & MA_WRITE)
+        flags |= VMA_PROT_WRITE;
+      if (mp->pr_mflags & MA_EXEC)
+        flags |= VMA_PROT_EXECUTE;
+      if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
+        {
+          /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
+             = [start,auxmap_start-1] u [auxmap_end,end-1].  */
+          if (start < auxmap_start)
+            if (callback (data, start, auxmap_start, flags))
+              break;
+          if (auxmap_end - 1 < end - 1)
+            if (callback (data, auxmap_end, end, flags))
+              break;
+        }
+      else
+        {
+          if (callback (data, start, end, flags))
+            break;
+        }
+    }
+  munmap (auxmap, memneed);
+  close (fd);
+  return 0;
+
+ fail1:
+  munmap (auxmap, memneed);
+ fail2:
+  close (fd);
+  return -1;
+
+# endif
+
 #elif HAVE_PSTAT_GETPROCVM /* HP-UX */
 
   unsigned long pagesize = getpagesize ();
--- a/lib/vma-iter.h	Sun Mar 19 14:19:07 2017 +0100
+++ b/lib/vma-iter.h	Sun Mar 19 15:45:26 2017 +0100
@@ -52,7 +52,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__ || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY
+#if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || defined __sun || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY
 # define VMA_ITERATE_SUPPORTED 1
 #endif