I pushed two more improvements of this module:

2017-09-26  Bruno Haible  <br...@clisp.org>

        vma-iter: Improvements for Linux and BSD platforms.
        - Add support for DragonFly BSD.
        - Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD.
        * lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close):
        Read the entire file into memory in a single system call.
        (vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD.
        * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD.

2017-09-26  Bruno Haible  <br...@clisp.org>

        vma-iter: Provide the protection flags on FreeBSD.
        * lib/vma-iter.c (vma_iterate) [FreeBSD]: When reading from /proc,
        skip three fields between the addresses and the protection flags.

>From aadb66ea37db2e9e016b6f5ba63e843f684b96fe Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 26 Sep 2017 17:03:48 +0200
Subject: [PATCH 1/2] vma-iter: Provide the protection flags on FreeBSD.

* lib/vma-iter.c (vma_iterate) [FreeBSD]: When reading from /proc,
skip three fields between the addresses and the protection flags.
---
 ChangeLog      |  6 ++++++
 lib/vma-iter.c | 23 +++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 440b906..9643700 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-09-26  Bruno Haible  <br...@clisp.org>
+
+	vma-iter: Provide the protection flags on FreeBSD.
+	* lib/vma-iter.c (vma_iterate) [FreeBSD]: When reading from /proc,
+	skip three fields between the addresses and the protection flags.
+
 2017-09-26  Paul Eggert  <egg...@cs.ucla.edu>
 
 	glob: remove bogus extern decl
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index d5c612f..ab2eb3f 100644
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -532,6 +532,29 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
                 && rof_getchar (&rof) == 'x'
                 && rof_scanf_lx (&rof, &end) >= 0))
             break;
+# if defined __FreeBSD__
+          /* Then the resident pages count.  */
+          do
+            c = rof_getchar (&rof);
+          while (c == ' ');
+          do
+            c = rof_getchar (&rof);
+          while (c != -1 && c != '\n' && c != ' ');
+          /* Then the private resident pages count.  */
+          do
+            c = rof_getchar (&rof);
+          while (c == ' ');
+          do
+            c = rof_getchar (&rof);
+          while (c != -1 && c != '\n' && c != ' ');
+          /* Then some kernel address.  */
+          do
+            c = rof_getchar (&rof);
+          while (c == ' ');
+          do
+            c = rof_getchar (&rof);
+          while (c != -1 && c != '\n' && c != ' ');
+# endif
           /* Then the flags.  */
           do
             c = rof_getchar (&rof);
-- 
2.7.4

>From 36ba2c05f02a7120be024b93e7b71af4972703a5 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 26 Sep 2017 19:48:39 +0200
Subject: [PATCH 2/2] vma-iter: Improvements for Linux and BSD platforms.

- Add support for DragonFly BSD.
- Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD.

* lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close):
Read the entire file into memory in a single system call.
(vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD.
* lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD.
---
 ChangeLog      |  10 +++
 lib/vma-iter.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++----------
 lib/vma-iter.h |   2 +-
 3 files changed, 173 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9643700..af67c6b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2017-09-26  Bruno Haible  <br...@clisp.org>
 
+	vma-iter: Improvements for Linux and BSD platforms.
+	- Add support for DragonFly BSD.
+	- Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD.
+	* lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close):
+	Read the entire file into memory in a single system call.
+	(vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD.
+	* lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD.
+
+2017-09-26  Bruno Haible  <br...@clisp.org>
+
 	vma-iter: Provide the protection flags on FreeBSD.
 	* lib/vma-iter.c (vma_iterate) [FreeBSD]: When reading from /proc,
 	skip three fields between the addresses and the protection flags.
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index ab2eb3f..961e5a2 100644
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -23,7 +23,12 @@
 #include <errno.h> /* errno */
 #include <stdlib.h> /* size_t */
 #include <fcntl.h> /* open, O_RDONLY */
-#include <unistd.h> /* getpagesize, read, close, getpid */
+#include <unistd.h> /* getpagesize, lseek, read, close, getpid */
+
+#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */
+# include <sys/types.h>
+# include <sys/mman.h> /* mmap, munmap */
+#endif
 
 #if defined __FreeBSD__ || defined __FreeBSD_kernel__ /* FreeBSD, GNU/kFreeBSD */
 # include <sys/types.h>
@@ -81,33 +86,131 @@
 
 /* Support for reading text files in the /proc file system.  */
 
-#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __NetBSD__ /* || defined __CYGWIN__ */
+#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */
 
 /* Buffered read-only streams.
    We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
-   call may call mmap() and thus pre-allocate available memory.  */
+   call may call mmap() and thus pre-allocate available memory.
+   Also, we cannot use multiple read() calls, because if the buffer size is
+   smaller than the file's contents:
+     - On NetBSD, the second read() call would return 0, thus making the file
+       appear truncated.
+     - On DragonFly BSD, the first read() call would fail with errno = EFBIG.
+     - On all platforms, if some other thread is doing memory allocations or
+       deallocations between two read() calls, there is a high risk that the
+       result of these two read() calls don't fit together, and as a
+       consequence we will parse gargage and either omit some VMAs or return
+       VMAs with nonsensical addresses.
+   So use mmap(), and ignore the resulting VMA.  */
+
+# ifdef TEST
+/* During testing, we want to run into the hairy cases.  */
+#  define STACK_ALLOCATED_BUFFER_SIZE 32
+# else
+#  define STACK_ALLOCATED_BUFFER_SIZE 1024
+# endif
 
 struct rofile
   {
-    int fd;
     size_t position;
     size_t filled;
     int eof_seen;
-    char buffer[1024];
+    /* These fields deal with allocation of the buffer.  */
+    char *buffer;
+    char *auxmap;
+    size_t auxmap_length;
+    unsigned long auxmap_start;
+    unsigned long auxmap_end;
+    char stack_allocated_buffer[STACK_ALLOCATED_BUFFER_SIZE];
   };
 
 /* Open a read-only file stream.  */
 static int
 rof_open (struct rofile *rof, const char *filename)
 {
-  int fd = open (filename, O_RDONLY);
+  int fd;
+  unsigned long pagesize;
+  size_t size;
+
+  fd = open (filename, O_RDONLY);
   if (fd < 0)
     return -1;
-  rof->fd = fd;
   rof->position = 0;
-  rof->filled = 0;
   rof->eof_seen = 0;
-  return 0;
+  /* Try the static buffer first.  */
+  pagesize = 0;
+  rof->buffer = rof->stack_allocated_buffer;
+  size = sizeof (rof->stack_allocated_buffer);
+  rof->auxmap = NULL;
+  rof->auxmap_start = 0;
+  rof->auxmap_end = 0;
+  for (;;)
+    {
+      /* Attempt to read the contents in a single system call.  */
+      {
+        int n = read (fd, rof->buffer, size);
+# ifdef EINTR
+        if (n < 0 && errno == EINTR)
+          goto retry;
+# endif
+# if defined __DragonFly__
+        if (!(n < 0 && errno == EFBIG))
+# endif
+          {
+            if (n <= 0)
+              /* Empty file.  */
+              goto fail1;
+            if (n < size)
+              {
+                /* The buffer was sufficiently large.  */
+                rof->filled = n;
+                close (fd);
+                return 0;
+              }
+          }
+      }
+      /* Allocate a larger buffer.  */
+      if (pagesize == 0)
+        {
+          pagesize = getpagesize ();
+          size = pagesize;
+        }
+      else
+        {
+          size = 2 * size;
+          if (size == 0)
+            /* Wraparound.  */
+            goto fail1;
+          if (rof->auxmap != NULL)
+            munmap (rof->auxmap, rof->auxmap_length);
+        }
+      rof->auxmap = (void *) mmap ((void *) 0, size, PROT_READ | PROT_WRITE,
+                                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+      if (rof->auxmap == (void *) -1)
+        {
+          close (fd);
+          return -1;
+        }
+      rof->auxmap_length = size;
+      rof->auxmap_start = (unsigned long) rof->auxmap;
+      rof->auxmap_end = rof->auxmap_start + size;
+      rof->buffer = (char *) rof->auxmap;
+     retry:
+      /* Restart.  */
+      if (lseek (fd, 0, SEEK_SET) < 0)
+        {
+          close (fd);
+          fd = open (filename, O_RDONLY);
+          if (fd < 0)
+            goto fail2;
+        }
+    }
+ fail1:
+  close (fd);
+ fail2:
+  if (rof->auxmap != NULL)
+    munmap (rof->auxmap, rof->auxmap_length);
+  return -1;
 }
 
 /* Return the next byte from a read-only file stream without consuming it,
@@ -117,25 +220,8 @@ rof_peekchar (struct rofile *rof)
 {
   if (rof->position == rof->filled)
     {
-      if (rof->eof_seen)
-        return -1;
-      else
-        for (;;)
-          {
-            int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
-# ifdef EINTR
-            if (n < 0 && errno == EINTR)
-              continue;
-# endif
-            if (n <= 0)
-              {
-                rof->eof_seen = 1;
-                return -1;
-              }
-            rof->filled = n;
-            rof->position = 0;
-            break;
-          }
+      rof->eof_seen = 1;
+      return -1;
     }
   return (unsigned char) rof->buffer[rof->position];
 }
@@ -180,7 +266,8 @@ rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
 static void
 rof_close (struct rofile *rof)
 {
-  close (rof->fd);
+  if (rof->auxmap != NULL)
+    munmap (rof->auxmap, rof->auxmap_length);
 }
 
 #endif
@@ -470,6 +557,9 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
   /* Open the current process' maps file.  It describes one VMA per line.  */
   if (rof_open (&rof, "/proc/self/maps") >= 0)
     {
+      unsigned long auxmap_start = rof.auxmap_start;
+      unsigned long auxmap_end = rof.auxmap_end;
+
       for (;;)
         {
           unsigned long start, end;
@@ -497,8 +587,22 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
           while (c = rof_getchar (&rof), c != -1 && c != '\n')
             ;
 
-          if (callback (data, start, end, flags))
-            break;
+          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;
+            }
         }
       rof_close (&rof);
       return 0;
@@ -507,13 +611,16 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
   /* Fallback if /proc is not accessible: Use sysctl().  */
   return vma_iterate_bsd (callback, data);
 
-#elif defined __FreeBSD__ || defined __NetBSD__
+#elif defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__
 
   struct rofile rof;
 
   /* Open the current process' maps file.  It describes one VMA per line.  */
   if (rof_open (&rof, "/proc/curproc/map") >= 0)
     {
+      unsigned long auxmap_start = rof.auxmap_start;
+      unsigned long auxmap_end = rof.auxmap_end;
+
       for (;;)
         {
           unsigned long start, end;
@@ -532,7 +639,7 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
                 && rof_getchar (&rof) == 'x'
                 && rof_scanf_lx (&rof, &end) >= 0))
             break;
-# if defined __FreeBSD__
+# if defined __FreeBSD__ || defined __DragonFly__
           /* Then the resident pages count.  */
           do
             c = rof_getchar (&rof);
@@ -571,8 +678,22 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
           while (c = rof_getchar (&rof), c != -1 && c != '\n')
             ;
 
-          if (callback (data, start, end, flags))
-            break;
+          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;
+            }
         }
       rof_close (&rof);
       return 0;
@@ -1279,4 +1400,10 @@ main ()
   return 0;
 }
 
+/*
+ * Local Variables:
+ * compile-command: "gcc -ggdb -DTEST -Wall -I.. vma-iter.c"
+ * End:
+ */
+
 #endif /* TEST */
diff --git a/lib/vma-iter.h b/lib/vma-iter.h
index c9e7165..2346972 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_kernel__ || defined __FreeBSD__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __sun && HAVE_SYS_PROCFS_H) || HAVE_PSTAT_GETPROCVM || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ || defined __BEOS__ || defined __HAIKU__ || HAVE_MQUERY
+#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __sgi || defined __osf__ || (defined __sun && HAVE_SYS_PROCFS_H) || 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

Reply via email to