summaryrefslogtreecommitdiff
path: root/lib/vma-iter.c
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2017-09-26 19:48:39 +0200
committerBruno Haible <bruno@clisp.org>2017-09-27 01:49:30 +0200
commit36ba2c05f02a7120be024b93e7b71af4972703a5 (patch)
tree3a0b703d33e850707967a488cc5974fe78189e56 /lib/vma-iter.c
parentaadb66ea37db2e9e016b6f5ba63e843f684b96fe (diff)
downloadgnulib-36ba2c05f02a7120be024b93e7b71af4972703a5.tar.gz
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.
Diffstat (limited to 'lib/vma-iter.c')
-rw-r--r--lib/vma-iter.c197
1 files changed, 162 insertions, 35 deletions
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index ab2eb3f45c..961e5a210c 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 */