>From fef4a135e413afbe21d8522e4d6a80a55cb8eec0 Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Sun, 19 Mar 2017 15:45:26 +0100
Subject: [PATCH 2/2] 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.
---
ChangeLog | 9 ++
lib/get-rusage-as.c | 4 +-
lib/get-rusage-data.c | 5 +-
lib/vma-iter.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++-
lib/vma-iter.h | 2 +-
5 files changed, 269 insertions(+), 4 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 311d45c..ee9e0d3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
2017-03-19 Bruno Haible
+ 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
+
vma-iter: Prefer HP-UX specific API on HP-UX.
* lib/vma-iter.c (vma_iterate): Move HP-UX specific implementation up.
* lib/vma-iter.h: Update.
diff --git a/lib/get-rusage-as.c b/lib/get-rusage-as.c
index fb9d218..3d9915d 100644
--- a/lib/get-rusage-as.c
+++ b/lib/get-rusage-as.c
@@ -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.
diff --git a/lib/get-rusage-data.c b/lib/get-rusage-data.c
index 4745ad6..62293ad 100644
--- a/lib/get-rusage-data.c
+++ b/lib/get-rusage-data.c
@@ -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.
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index 2a12449..7356188 100644
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -23,7 +23,7 @@
#include /* errno */
#include /* size_t */
#include /* open, O_RDONLY */
-#include /* getpagesize, read, close */
+#include /* getpagesize, read, close, getpid */
#if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
# include /* memcpy */
@@ -32,6 +32,15 @@
# include /* PIOC*, prmap_t */
#endif
+#if defined __sun /* Solaris */
+# include /* memcpy */
+# include
+# include /* mmap, munmap */
+/* Try to use the newer ("structured") /proc filesystem API, if supported. */
+# define _STRUCTURED_PROC 1
+# include /* prmap_t, optionally PIOC* */
+#endif
+
#if HAVE_PSTAT_GETPROCVM /* HP-UX */
# include /* pstat_getprocvm */
#endif
@@ -369,6 +378,248 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
close (fd);
return -1;
+#elif defined __sun /* Solaris */
+
+ /* Note: Solaris 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//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 ();
diff --git a/lib/vma-iter.h b/lib/vma-iter.h
index b1acaa2..bb1f3c1 100644
--- a/lib/vma-iter.h
+++ b/lib/vma-iter.h
@@ -52,7 +52,7 @@ extern int vma_iterate (vma_iterate_callback_fn callback, void *data);
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
--
2.7.4