#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>
/* 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,
{
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];
}
static void
rof_close (struct rofile *rof)
{
- close (rof->fd);
+ if (rof->auxmap != NULL)
+ munmap (rof->auxmap, rof->auxmap_length);
}
#endif
/* 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;
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;
/* 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;
&& 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);
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;
return 0;
}
+/*
+ * Local Variables:
+ * compile-command: "gcc -ggdb -DTEST -Wall -I.. vma-iter.c"
+ * End:
+ */
+
#endif /* TEST */