1 /* Getter for RLIMIT_AS.
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2011.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "resource-ext.h"
23 /* The "address space size" is defined as the total size of the virtual memory
24 areas of the current process. This includes
25 - areas belonging to the executable and shared libraries,
26 - areas allocated by malloc() or mmap(),
27 - the stack and environment areas,
28 - gaps and guard pages (mappings with PROT_NONE),
29 - other system dependent areas, such as vsyscall or vdso on Linux.
31 There are two ways of retrieving the current address space size:
32 a) by trying setrlimit with various values and observing whether the
33 kernel allows additional mmap calls,
34 b) by using system dependent APIs that allow to iterate over the list
35 of virtual memory areas.
36 We don't use the mincore() based approach here, because it would be very
37 slow when applied to an entire address space, especially on 64-bit
39 We define two functions
40 get_rusage_as_via_setrlimit(),
41 get_rusage_as_via_iterator().
43 Discussion per platform:
46 a) setrlimit with RLIMIT_AS works.
47 b) The /proc/self/maps file contains a list of the virtual memory areas.
48 Both methods agree, except that on x86_64 systems, the value of
49 get_rusage_as_via_iterator() is 4 KB higher than
50 get_rusage_as_via_setrlimit().
53 a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
54 ignores RLIMIT_AS. mmap() of a page always succeeds, therefore
55 get_rusage_as_via_setrlimit() is always 0.
56 b) The Mach based API works.
59 a) setrlimit with RLIMIT_AS works.
60 b) The /proc/self/maps file contains a list of the virtual memory areas.
63 a) setrlimit with RLIMIT_AS works.
64 b) No VMA iteration API exists.
67 a) setrlimit exists, but RLIMIT_AS is not defined.
68 b) No VMA iteration API exists.
71 a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS
72 apparently ignores RLIMIT_AS. mmap() of a page always succeeds,
73 therefore get_rusage_as_via_setrlimit() is always 0.
74 b) No VMA iteration API exists.
77 a) setrlimit with RLIMIT_AS works.
78 b) No VMA iteration API exists.
81 a) setrlimit with RLIMIT_AS works.
82 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
86 a) setrlimit with RLIMIT_AS works.
87 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP.
88 The value returned by get_rusage_as_via_setrlimit() is 64 KB higher than
89 get_rusage_as_via_iterator(). It's not clear why.
92 a) setrlimit with RLIMIT_AS works.
93 b) No VMA iteration API exists.
96 a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000.
97 get_rusage_as_via_setrlimit() therefore produces a wrong value.
98 b) The /proc/$pid/maps file lists only the memory areas belonging to
99 the executable and shared libraries, not the anonymous memory.
100 But the native Win32 API works.
103 a) There is no setrlimit function.
104 b) The native Win32 API works.
107 a) On BeOS, there is no setrlimit function.
108 On Haiku, setrlimit exists. RLIMIT_AS is defined but unsupported.
109 b) There is a specific BeOS API: get_next_area_info().
113 #include <errno.h> /* errno */
114 #include <stdlib.h> /* size_t, abort */
115 #include <fcntl.h> /* open, O_RDONLY */
116 #include <unistd.h> /* getpagesize, read, close */
119 /* System support for get_rusage_as_via_setrlimit(). */
122 # include <sys/time.h>
123 # include <sys/resource.h> /* getrlimit, setrlimit */
126 /* Test whether mmap() and mprotect() are available.
127 We don't use HAVE_MMAP, because AC_FUNC_MMAP would not define it on HP-UX.
128 HAVE_MPROTECT is not enough, because mingw does not have mmap() but has an
129 mprotect() function in libgcc.a. */
130 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT
132 # include <sys/types.h>
133 # include <sys/mman.h> /* mmap, munmap */
134 /* Define MAP_FILE when it isn't otherwise. */
141 /* System support for get_rusage_as_via_iterator(). */
143 #if defined __sgi || defined __osf__ /* IRIX, OSF/1 */
144 # include <string.h> /* memcpy */
145 # include <sys/types.h>
146 # include <sys/procfs.h> /* PIOC*, prmap_t */
149 #if defined __APPLE__ && defined __MACH__ /* MacOS X */
150 # include <mach/mach.h>
153 #if (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ /* Windows */
154 # include <windows.h>
157 #if defined __BEOS__ /* BeOS */
162 #if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
164 static inline uintptr_t
165 get_rusage_as_via_setrlimit (void)
169 struct rlimit orig_limit;
171 # if HAVE_MAP_ANONYMOUS
172 const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
174 # else /* !HAVE_MAP_ANONYMOUS */
175 const int flags = MAP_FILE | MAP_PRIVATE;
176 int fd = open ("/dev/zero", O_RDONLY, 0666);
181 /* Record the original limit. */
182 if (getrlimit (RLIMIT_AS, &orig_limit) < 0)
188 if (orig_limit.rlim_max != RLIM_INFINITY
189 && (orig_limit.rlim_cur == RLIM_INFINITY
190 || orig_limit.rlim_cur > orig_limit.rlim_max))
191 /* We may not be able to restore the current rlim_cur value.
199 /* The granularity is a single page. */
200 const size_t pagesize = getpagesize ();
202 uintptr_t low_bound = 0;
203 uintptr_t high_bound;
207 /* Here we know that the address space size is >= low_bound. */
208 struct rlimit try_limit;
209 uintptr_t try_next = 2 * low_bound + pagesize;
211 if (try_next < low_bound)
213 try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize;
215 /* There's no point in trying a value > orig_limit.rlim_max, as
216 setrlimit would fail anyway. */
217 if (orig_limit.rlim_max != RLIM_INFINITY
218 && orig_limit.rlim_max < try_next)
219 try_next = orig_limit.rlim_max;
221 /* Avoid endless loop. */
222 if (try_next == low_bound)
224 /* try_next could not be increased. */
229 try_limit.rlim_max = orig_limit.rlim_max;
230 try_limit.rlim_cur = try_next;
231 if (setrlimit (RLIMIT_AS, &try_limit) == 0)
233 /* Allocate a page of memory, to compare the current address space
234 size with try_limit.rlim_cur. */
236 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
238 if (new_page != (void *)(-1))
240 /* The page could be added successfully. Free it. */
241 if (munmap (new_page, pagesize) < 0)
243 /* We know that the address space size is
244 < try_limit.rlim_cur. */
245 high_bound = try_next;
250 /* We know that the address space size is
251 >= try_limit.rlim_cur. */
252 low_bound = try_next;
257 /* Here we expect only EINVAL, not EPERM. */
260 /* We know that the address space size is
261 >= try_limit.rlim_cur. */
262 low_bound = try_next;
266 /* Here we know that the address space size is
267 >= low_bound and < high_bound. */
268 while (high_bound - low_bound > pagesize)
270 struct rlimit try_limit;
272 low_bound + (((high_bound - low_bound) / 2) / pagesize) * pagesize;
274 /* Here low_bound <= try_next < high_bound. */
275 try_limit.rlim_max = orig_limit.rlim_max;
276 try_limit.rlim_cur = try_next;
277 if (setrlimit (RLIMIT_AS, &try_limit) == 0)
279 /* Allocate a page of memory, to compare the current address space
280 size with try_limit.rlim_cur. */
282 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0);
284 if (new_page != (void *)(-1))
286 /* The page could be added successfully. Free it. */
287 if (munmap (new_page, pagesize) < 0)
289 /* We know that the address space size is
290 < try_limit.rlim_cur. */
291 high_bound = try_next;
295 /* We know that the address space size is
296 >= try_limit.rlim_cur. */
297 low_bound = try_next;
302 /* Here we expect only EINVAL, not EPERM. */
305 /* We know that the address space size is
306 >= try_limit.rlim_cur. */
307 low_bound = try_next;
314 /* Restore the original rlim_cur value. */
315 if (setrlimit (RLIMIT_AS, &orig_limit) < 0)
319 # if !HAVE_MAP_ANONYMOUS
328 /* Support for reading text files in the /proc file system. */
330 #if defined __linux__ || defined __FreeBSD__ /* || defined __CYGWIN__ */
332 /* Buffered read-only streams.
333 We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
334 call may call mmap() and thus pre-allocate available memory. */
345 /* Open a read-only file stream. */
347 rof_open (struct rofile *rof, const char *filename)
349 int fd = open (filename, O_RDONLY);
359 /* Return the next byte from a read-only file stream without consuming it,
362 rof_peekchar (struct rofile *rof)
364 if (rof->position == rof->filled)
371 int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
373 if (n < 0 && errno == EINTR)
386 return (unsigned char) rof->buffer[rof->position];
389 /* Return the next byte from a read-only file stream, or -1 at EOF. */
391 rof_getchar (struct rofile *rof)
393 int c = rof_peekchar (rof);
399 /* Parse an unsigned hexadecimal number from a read-only file stream. */
401 rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
403 unsigned long value = 0;
404 unsigned int numdigits = 0;
407 int c = rof_peekchar (rof);
408 if (c >= '0' && c <= '9')
409 value = (value << 4) + (c - '0');
410 else if (c >= 'A' && c <= 'F')
411 value = (value << 4) + (c - 'A' + 10);
412 else if (c >= 'a' && c <= 'f')
413 value = (value << 4) + (c - 'a' + 10);
425 /* Close a read-only file stream. */
427 rof_close (struct rofile *rof)
435 static inline uintptr_t
436 get_rusage_as_via_iterator (void)
438 #if defined __linux__ /* || defined __CYGWIN__ */
444 /* Open the current process' maps file. It describes one VMA per line. */
445 if (rof_open (&rof, "/proc/self/maps") < 0)
451 unsigned long start, end;
453 if (!(rof_scanf_lx (&rof, &start) >= 0
454 && rof_getchar (&rof) == '-'
455 && rof_scanf_lx (&rof, &end) >= 0))
457 while (c = rof_getchar (&rof), c != -1 && c != '\n')
459 total += end - start;
464 #elif defined __FreeBSD__
470 /* Open the current process' maps file. It describes one VMA per line. */
471 if (rof_open (&rof, "/proc/curproc/map") < 0)
477 unsigned long start, end;
479 if (!(rof_getchar (&rof) == '0'
480 && rof_getchar (&rof) == 'x'
481 && rof_scanf_lx (&rof, &start) >= 0))
483 while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
485 if (!(rof_getchar (&rof) == '0'
486 && rof_getchar (&rof) == 'x'
487 && rof_scanf_lx (&rof, &end) >= 0))
489 while (c = rof_getchar (&rof), c != -1 && c != '\n')
491 total += end - start;
496 #elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
499 char fnamebuf[6+10+1];
504 # if HAVE_MAP_ANONYMOUS
506 # define map_flags MAP_ANONYMOUS
512 unsigned long auxmap_start;
513 unsigned long auxmap_end;
518 pagesize = getpagesize ();
520 /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */
521 fname = fnamebuf + sizeof (fnamebuf) - 1;
524 unsigned int value = getpid ();
526 *--fname = (value % 10) + '0';
527 while ((value = value / 10) > 0);
530 memcpy (fname, "/proc/", 6);
532 fd = open (fname, O_RDONLY);
536 if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
539 memneed = (nmaps + 10) * sizeof (prmap_t);
540 /* Allocate memneed bytes of memory.
541 We cannot use alloca here, because not much stack space is guaranteed.
542 We also cannot use malloc here, because a malloc() call may call mmap()
543 and thus pre-allocate available memory.
544 So use mmap(), and ignore the resulting VMA. */
545 memneed = ((memneed - 1) / pagesize + 1) * pagesize;
546 # if !HAVE_MAP_ANONYMOUS
547 zero_fd = open ("/dev/zero", O_RDONLY, 0644);
551 auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
552 map_flags | MAP_PRIVATE, zero_fd, 0);
553 # if !HAVE_MAP_ANONYMOUS
556 if (auxmap == (void *) -1)
558 auxmap_start = (unsigned long) auxmap;
559 auxmap_end = auxmap_start + memneed;
560 maps = (prmap_t *) auxmap;
562 if (ioctl (fd, PIOCMAP, maps) < 0)
568 unsigned long start, end;
570 start = (unsigned long) mp->pr_vaddr;
571 end = start + mp->pr_size;
572 if (start == 0 && end == 0)
575 if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
576 /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
577 = [start,auxmap_start-1] u [auxmap_end,end-1]. */
578 total += (end - start) - memneed;
580 total += end - start;
582 munmap (auxmap, memneed);
587 munmap (auxmap, memneed);
592 #elif defined __APPLE__ && defined __MACH__ /* MacOS X */
594 task_t task = mach_task_self ();
595 vm_address_t address;
597 vm_address_t total = 0;
599 for (address = VM_MIN_ADDRESS;; address += size)
602 mach_port_t object_name;
603 /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
604 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
605 mach_vm_address_t and mach_vm_size_t are always 64 bits large.
606 MacOS X 10.5 has three vm_region like methods:
607 - vm_region. It has arguments that depend on whether the current
608 process is 32-bit or 64-bit. When linking dynamically, this
609 function exists only in 32-bit processes. Therefore we use it only
611 - vm_region_64. It has arguments that depend on whether the current
612 process is 32-bit or 64-bit. It interprets a flavor
613 VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
614 dangerous since 'struct vm_region_basic_info_64' is larger than
615 'struct vm_region_basic_info'; therefore let's write
616 VM_REGION_BASIC_INFO_64 explicitly.
617 - mach_vm_region. It has arguments that are 64-bit always. This
618 function is useful when you want to access the VM of a process
619 other than the current process.
620 In 64-bit processes, we could use vm_region_64 or mach_vm_region.
621 I choose vm_region_64 because it uses the same types as vm_region,
622 resulting in less conditional code. */
623 # if defined __ppc64__ || defined __x86_64__
624 struct vm_region_basic_info_64 info;
625 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
627 more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
628 (vm_region_info_t)&info, &info_count, &object_name)
631 struct vm_region_basic_info info;
632 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
634 more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
635 (vm_region_info_t)&info, &info_count, &object_name)
638 if (object_name != MACH_PORT_NULL)
639 mach_port_deallocate (mach_task_self (), object_name);
646 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
647 /* Windows platform. Use the native Windows API. */
649 MEMORY_BASIC_INFORMATION info;
650 unsigned long address = 0;
651 unsigned long total = 0;
653 while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
655 if (info.State != MEM_FREE)
656 total += info.RegionSize;
657 address = (unsigned long)info.BaseAddress + info.RegionSize;
661 #elif defined __BEOS__
662 /* Use the BeOS specific API. */
666 unsigned long total = 0;
669 while (get_next_area_info (0, &cookie, &info) == B_OK)
671 unsigned long start, end;
673 start = (unsigned long) info.address;
674 end = start + info.size;
676 total += end - start;
691 #if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* MacOS X, AIX, Cygwin */
692 /* get_rusage_as_via_setrlimit() does not work.
693 Prefer get_rusage_as_via_iterator(). */
694 return get_rusage_as_via_iterator ();
695 #elif HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
696 /* Prefer get_rusage_as_via_setrlimit() if it succeeds,
697 because the caller may want to use the result with setrlimit(). */
700 result = get_rusage_as_via_setrlimit ();
702 result = get_rusage_as_via_iterator ();
705 return get_rusage_as_via_iterator ();