diff options
author | Bruno Haible <bruno@clisp.org> | 2017-09-26 19:48:39 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2017-09-27 01:49:30 +0200 |
commit | 36ba2c05f02a7120be024b93e7b71af4972703a5 (patch) | |
tree | 3a0b703d33e850707967a488cc5974fe78189e56 /lib/vma-iter.c | |
parent | aadb66ea37db2e9e016b6f5ba63e843f684b96fe (diff) | |
download | gnulib-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.c | 197 |
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 */ |