>From 36ba2c05f02a7120be024b93e7b71af4972703a5 Mon Sep 17 00:00:00 2001
From: Bruno Haible
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
+ 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
+
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 */
#include /* size_t */
#include /* open, O_RDONLY */
-#include /* getpagesize, read, close, getpid */
+#include /* getpagesize, lseek, read, close, getpid */
+
+#if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */
+# include
+# include /* mmap, munmap */
+#endif
#if defined __FreeBSD__ || defined __FreeBSD_kernel__ /* FreeBSD, GNU/kFreeBSD */
# include
@@ -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 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