/* Getter for RLIMIT_AS.
- Copyright (C) 2011 Free Software Foundation, Inc.
+ Copyright (C) 2011-2013 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2011.
This program is free software: you can redistribute it and/or modify
get_rusage_as_via_iterator() is 4 KB higher than
get_rusage_as_via_setrlimit().
- MacOS X:
+ Mac OS X:
a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
ignores RLIMIT_AS. mmap() of a page always succeeds, therefore
get_rusage_as_via_setrlimit() is always 0.
NetBSD:
a) setrlimit with RLIMIT_AS works.
- b) No VMA iteration API exists.
+ b) The /proc/self/maps file contains a list of the virtual memory areas.
+ Both methods agree,
OpenBSD:
a) setrlimit exists, but RLIMIT_AS is not defined.
- b) No VMA iteration API exists.
+ b) mquery() can be used to find out about the virtual memory areas.
AIX:
a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
get_rusage_as_via_setrlimit() therefore produces a wrong value.
b) The /proc/$pid/maps file lists only the memory areas belonging to
the executable and shared libraries, not the anonymous memory.
- But the native Win32 API works.
+ But the native Windows API works.
mingw:
a) There is no setrlimit function.
- b) The native Win32 API works.
+ b) The native Windows API works.
BeOS, Haiku:
a) On BeOS, there is no setrlimit function.
/* System support for get_rusage_as_via_iterator(). */
-#if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
-# include <string.h> /* memcpy */
-# include <sys/types.h>
-# include <sys/procfs.h> /* PIOC*, prmap_t */
-#endif
-
-#if defined __APPLE__ && defined __MACH__ /* MacOS X */
-# include <mach/mach.h>
-#endif
-
-#if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */
-# include <windows.h>
-#endif
-
-#if defined __BEOS__ /* BeOS */
-# include <OS.h>
-#endif
+#include "vma-iter.h"
#if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
-static inline uintptr_t
+static uintptr_t
get_rusage_as_via_setrlimit (void)
{
uintptr_t result;
if (getrlimit (RLIMIT_AS, &orig_limit) < 0)
{
result = 0;
- goto done;
+ goto done2;
}
if (orig_limit.rlim_max != RLIM_INFINITY
So bail out. */
{
result = 0;
- goto done;
+ goto done2;
}
{
{
/* try_next could not be increased. */
result = low_bound;
- goto done;
+ goto done1;
}
try_limit.rlim_max = orig_limit.rlim_max;
result = low_bound;
}
+ done1:
/* Restore the original rlim_cur value. */
if (setrlimit (RLIMIT_AS, &orig_limit) < 0)
abort ();
- done:
+ done2:
# if !HAVE_MAP_ANONYMOUS
close (fd);
# endif
return result;
}
-#endif
-
-
-/* Support for reading text files in the /proc file system. */
-
-#if defined __linux__ || defined __FreeBSD__ /* || 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. */
+#else
-struct rofile
- {
- int fd;
- size_t position;
- size_t filled;
- int eof_seen;
- char buffer[1024];
- };
-
-/* Open a read-only file stream. */
-static int
-rof_open (struct rofile *rof, const char *filename)
+static uintptr_t
+get_rusage_as_via_setrlimit (void)
{
- int 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;
}
-/* Return the next byte from a read-only file stream without consuming it,
- or -1 at EOF. */
-static int
-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;
- }
- }
- return (unsigned char) rof->buffer[rof->position];
-}
+#endif
-/* Return the next byte from a read-only file stream, or -1 at EOF. */
-static int
-rof_getchar (struct rofile *rof)
-{
- int c = rof_peekchar (rof);
- if (c >= 0)
- rof->position++;
- return c;
-}
-/* Parse an unsigned hexadecimal number from a read-only file stream. */
+#if VMA_ITERATE_SUPPORTED
+
static int
-rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
+vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
+ unsigned int flags)
{
- unsigned long value = 0;
- unsigned int numdigits = 0;
- for (;;)
- {
- int c = rof_peekchar (rof);
- if (c >= '0' && c <= '9')
- value = (value << 4) + (c - '0');
- else if (c >= 'A' && c <= 'F')
- value = (value << 4) + (c - 'A' + 10);
- else if (c >= 'a' && c <= 'f')
- value = (value << 4) + (c - 'a' + 10);
- else
- break;
- rof_getchar (rof);
- numdigits++;
- }
- if (numdigits == 0)
- return -1;
- *valuep = value;
- return 0;
-}
+ uintptr_t *totalp = (uintptr_t *) data;
-/* Close a read-only file stream. */
-static void
-rof_close (struct rofile *rof)
-{
- close (rof->fd);
+ *totalp += end - start;
+ return 0;
}
-#endif
-
-
-static inline uintptr_t
+static uintptr_t
get_rusage_as_via_iterator (void)
{
-#if defined __linux__ /* || defined __CYGWIN__ */
-
- struct rofile rof;
- int c;
- unsigned long total;
-
- /* Open the current process' maps file. It describes one VMA per line. */
- if (rof_open (&rof, "/proc/self/maps") < 0)
- return 0;
-
- total = 0;
- for (;;)
- {
- unsigned long start, end;
-
- if (!(rof_scanf_lx (&rof, &start) >= 0
- && rof_getchar (&rof) == '-'
- && rof_scanf_lx (&rof, &end) >= 0))
- break;
- while (c = rof_getchar (&rof), c != -1 && c != '\n')
- ;
- total += end - start;
- }
- rof_close (&rof);
- return total;
-
-#elif defined __FreeBSD__
-
- struct rofile rof;
- int c;
- unsigned long total;
-
- /* Open the current process' maps file. It describes one VMA per line. */
- if (rof_open (&rof, "/proc/curproc/map") < 0)
- return 0;
-
- total = 0;
- for (;;)
- {
- unsigned long start, end;
-
- if (!(rof_getchar (&rof) == '0'
- && rof_getchar (&rof) == 'x'
- && rof_scanf_lx (&rof, &start) >= 0))
- break;
- while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
- rof_getchar (&rof);
- if (!(rof_getchar (&rof) == '0'
- && rof_getchar (&rof) == 'x'
- && rof_scanf_lx (&rof, &end) >= 0))
- break;
- while (c = rof_getchar (&rof), c != -1 && c != '\n')
- continue;
- total += end - start;
- }
- rof_close (&rof);
- return total;
-
-#elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
-
- size_t pagesize;
- char fnamebuf[6+10+1];
- char *fname;
- int fd;
- int nmaps;
- size_t memneed;
-# if HAVE_MAP_ANONYMOUS
-# define zero_fd -1
-# define map_flags MAP_ANONYMOUS
-# else
- int zero_fd;
-# define map_flags 0
-# endif
- void *auxmap;
- unsigned long auxmap_start;
- unsigned long auxmap_end;
- prmap_t* maps;
- prmap_t* mp;
- unsigned long total;
-
- pagesize = getpagesize ();
-
- /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */
- fname = fnamebuf + sizeof (fnamebuf) - 1;
- *fname = '\0';
- {
- unsigned int value = getpid ();
- do
- *--fname = (value % 10) + '0';
- while ((value = value / 10) > 0);
- }
- fname -= 6;
- memcpy (fname, "/proc/", 6);
-
- fd = open (fname, O_RDONLY);
- if (fd < 0)
- return 0;
-
- if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
- goto fail2;
-
- memneed = (nmaps + 10) * sizeof (prmap_t);
- /* Allocate memneed bytes of memory.
- We cannot use alloca here, because not much stack space is guaranteed.
- We also cannot use malloc here, because a malloc() call may call mmap()
- and thus pre-allocate available memory.
- So use mmap(), and ignore the resulting VMA. */
- memneed = ((memneed - 1) / pagesize + 1) * pagesize;
-# if !HAVE_MAP_ANONYMOUS
- zero_fd = open ("/dev/zero", O_RDONLY, 0644);
- if (zero_fd < 0)
- goto fail2;
-# endif
- auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
- map_flags | MAP_PRIVATE, zero_fd, 0);
-# if !HAVE_MAP_ANONYMOUS
- close (zero_fd);
-# endif
- if (auxmap == (void *) -1)
- goto fail2;
- auxmap_start = (unsigned long) auxmap;
- auxmap_end = auxmap_start + memneed;
- maps = (prmap_t *) auxmap;
-
- if (ioctl (fd, PIOCMAP, maps) < 0)
- goto fail1;
-
- total = 0;
- for (mp = maps;;)
- {
- unsigned long start, end;
-
- start = (unsigned long) mp->pr_vaddr;
- end = start + mp->pr_size;
- if (start == 0 && end == 0)
- break;
- mp++;
- 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]. */
- total += (end - start) - memneed;
- else
- total += end - start;
- }
- munmap (auxmap, memneed);
- close (fd);
- return total;
-
- fail1:
- munmap (auxmap, memneed);
- fail2:
- close (fd);
- return 0;
-
-#elif defined __APPLE__ && defined __MACH__ /* MacOS X */
-
- task_t task = mach_task_self ();
- vm_address_t address;
- vm_size_t size;
- vm_address_t total = 0;
+ uintptr_t total = 0;
- for (address = VM_MIN_ADDRESS;; address += size)
- {
- int more;
- mach_port_t object_name;
- /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
- 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
- mach_vm_address_t and mach_vm_size_t are always 64 bits large.
- MacOS X 10.5 has three vm_region like methods:
- - vm_region. It has arguments that depend on whether the current
- process is 32-bit or 64-bit. When linking dynamically, this
- function exists only in 32-bit processes. Therefore we use it only
- in 32-bit processes.
- - vm_region_64. It has arguments that depend on whether the current
- process is 32-bit or 64-bit. It interprets a flavor
- VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
- dangerous since 'struct vm_region_basic_info_64' is larger than
- 'struct vm_region_basic_info'; therefore let's write
- VM_REGION_BASIC_INFO_64 explicitly.
- - mach_vm_region. It has arguments that are 64-bit always. This
- function is useful when you want to access the VM of a process
- other than the current process.
- In 64-bit processes, we could use vm_region_64 or mach_vm_region.
- I choose vm_region_64 because it uses the same types as vm_region,
- resulting in less conditional code. */
-# if defined __ppc64__ || defined __x86_64__
- struct vm_region_basic_info_64 info;
- mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
-
- more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
- (vm_region_info_t)&info, &info_count, &object_name)
- == KERN_SUCCESS);
-# else
- struct vm_region_basic_info info;
- mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
-
- more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
- (vm_region_info_t)&info, &info_count, &object_name)
- == KERN_SUCCESS);
-# endif
- if (object_name != MACH_PORT_NULL)
- mach_port_deallocate (mach_task_self (), object_name);
- if (!more)
- break;
- total += size;
- }
- return total;
+ vma_iterate (vma_iterate_callback, &total);
-#elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
- /* Windows platform. Use the native Windows API. */
-
- MEMORY_BASIC_INFORMATION info;
- unsigned long address = 0;
- unsigned long total = 0;
-
- while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
- {
- if (info.State != MEM_FREE)
- total += info.RegionSize;
- address = (unsigned long)info.BaseAddress + info.RegionSize;
- }
- return total;
-
-#elif defined __BEOS__
- /* Use the BeOS specific API. */
-
- area_info info;
- int32 cookie;
- unsigned long total = 0;
-
- cookie = 0;
- while (get_next_area_info (0, &cookie, &info) == B_OK)
- {
- unsigned long start, end;
-
- start = (unsigned long) info.address;
- end = start + info.size;
-
- total += end - start;
- }
return total;
+}
#else
+static uintptr_t
+get_rusage_as_via_iterator (void)
+{
return 0;
+}
#endif
-}
uintptr_t
get_rusage_as (void)
{
-#if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* MacOS X, AIX, Cygwin */
+#if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* Mac OS X, AIX, Cygwin */
/* get_rusage_as_via_setrlimit() does not work.
Prefer get_rusage_as_via_iterator(). */
return get_rusage_as_via_iterator ();
return get_rusage_as_via_iterator ();
#endif
}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main ()
+{
+ printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x88);
+ printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x8812);
+ printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ malloc (0x281237);
+ printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n",
+ get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (),
+ get_rusage_as ());
+ return 0;
+}
+
+#endif /* TEST */