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;
315 /* Restore the original rlim_cur value. */
316 if (setrlimit (RLIMIT_AS, &orig_limit) < 0)
320 # if !HAVE_MAP_ANONYMOUS
329 /* Support for reading text files in the /proc file system. */
331 #if defined __linux__ || defined __FreeBSD__ /* || defined __CYGWIN__ */
333 /* Buffered read-only streams.
334 We cannot use <stdio.h> here, because fopen() calls malloc(), and a malloc()
335 call may call mmap() and thus pre-allocate available memory. */
346 /* Open a read-only file stream. */
348 rof_open (struct rofile *rof, const char *filename)
350 int fd = open (filename, O_RDONLY);
360 /* Return the next byte from a read-only file stream without consuming it,
363 rof_peekchar (struct rofile *rof)
365 if (rof->position == rof->filled)
372 int n = read (rof->fd, rof->buffer, sizeof (rof->buffer));
374 if (n < 0 && errno == EINTR)
387 return (unsigned char) rof->buffer[rof->position];
390 /* Return the next byte from a read-only file stream, or -1 at EOF. */
392 rof_getchar (struct rofile *rof)
394 int c = rof_peekchar (rof);
400 /* Parse an unsigned hexadecimal number from a read-only file stream. */
402 rof_scanf_lx (struct rofile *rof, unsigned long *valuep)
404 unsigned long value = 0;
405 unsigned int numdigits = 0;
408 int c = rof_peekchar (rof);
409 if (c >= '0' && c <= '9')
410 value = (value << 4) + (c - '0');
411 else if (c >= 'A' && c <= 'F')
412 value = (value << 4) + (c - 'A' + 10);
413 else if (c >= 'a' && c <= 'f')
414 value = (value << 4) + (c - 'a' + 10);
426 /* Close a read-only file stream. */
428 rof_close (struct rofile *rof)
436 static inline uintptr_t
437 get_rusage_as_via_iterator (void)
439 #if defined __linux__ /* || defined __CYGWIN__ */
445 /* Open the current process' maps file. It describes one VMA per line. */
446 if (rof_open (&rof, "/proc/self/maps") < 0)
452 unsigned long start, end;
454 if (!(rof_scanf_lx (&rof, &start) >= 0
455 && rof_getchar (&rof) == '-'
456 && rof_scanf_lx (&rof, &end) >= 0))
458 while (c = rof_getchar (&rof), c != -1 && c != '\n')
460 total += end - start;
465 #elif defined __FreeBSD__
471 /* Open the current process' maps file. It describes one VMA per line. */
472 if (rof_open (&rof, "/proc/curproc/map") < 0)
478 unsigned long start, end;
480 if (!(rof_getchar (&rof) == '0'
481 && rof_getchar (&rof) == 'x'
482 && rof_scanf_lx (&rof, &start) >= 0))
484 while (c = rof_peekchar (&rof), c == ' ' || c == '\t')
486 if (!(rof_getchar (&rof) == '0'
487 && rof_getchar (&rof) == 'x'
488 && rof_scanf_lx (&rof, &end) >= 0))
490 while (c = rof_getchar (&rof), c != -1 && c != '\n')
492 total += end - start;
497 #elif defined __sgi || defined __osf__ /* IRIX, OSF/1 */
500 char fnamebuf[6+10+1];
505 # if HAVE_MAP_ANONYMOUS
507 # define map_flags MAP_ANONYMOUS
513 unsigned long auxmap_start;
514 unsigned long auxmap_end;
519 pagesize = getpagesize ();
521 /* Construct fname = sprintf (fnamebuf+i, "/proc/%u", getpid ()). */
522 fname = fnamebuf + sizeof (fnamebuf) - 1;
525 unsigned int value = getpid ();
527 *--fname = (value % 10) + '0';
528 while ((value = value / 10) > 0);
531 memcpy (fname, "/proc/", 6);
533 fd = open (fname, O_RDONLY);
537 if (ioctl (fd, PIOCNMAP, &nmaps) < 0)
540 memneed = (nmaps + 10) * sizeof (prmap_t);
541 /* Allocate memneed bytes of memory.
542 We cannot use alloca here, because not much stack space is guaranteed.
543 We also cannot use malloc here, because a malloc() call may call mmap()
544 and thus pre-allocate available memory.
545 So use mmap(), and ignore the resulting VMA. */
546 memneed = ((memneed - 1) / pagesize + 1) * pagesize;
547 # if !HAVE_MAP_ANONYMOUS
548 zero_fd = open ("/dev/zero", O_RDONLY, 0644);
552 auxmap = (void *) mmap ((void *) 0, memneed, PROT_READ | PROT_WRITE,
553 map_flags | MAP_PRIVATE, zero_fd, 0);
554 # if !HAVE_MAP_ANONYMOUS
557 if (auxmap == (void *) -1)
559 auxmap_start = (unsigned long) auxmap;
560 auxmap_end = auxmap_start + memneed;
561 maps = (prmap_t *) auxmap;
563 if (ioctl (fd, PIOCMAP, maps) < 0)
569 unsigned long start, end;
571 start = (unsigned long) mp->pr_vaddr;
572 end = start + mp->pr_size;
573 if (start == 0 && end == 0)
576 if (start <= auxmap_start && auxmap_end - 1 <= end - 1)
577 /* Consider [start,end-1] \ [auxmap_start,auxmap_end-1]
578 = [start,auxmap_start-1] u [auxmap_end,end-1]. */
579 total += (end - start) - memneed;
581 total += end - start;
583 munmap (auxmap, memneed);
588 munmap (auxmap, memneed);
593 #elif defined __APPLE__ && defined __MACH__ /* MacOS X */
595 task_t task = mach_task_self ();
596 vm_address_t address;
598 vm_address_t total = 0;
600 for (address = VM_MIN_ADDRESS;; address += size)
603 mach_port_t object_name;
604 /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
605 32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
606 mach_vm_address_t and mach_vm_size_t are always 64 bits large.
607 MacOS X 10.5 has three vm_region like methods:
608 - vm_region. It has arguments that depend on whether the current
609 process is 32-bit or 64-bit. When linking dynamically, this
610 function exists only in 32-bit processes. Therefore we use it only
612 - vm_region_64. It has arguments that depend on whether the current
613 process is 32-bit or 64-bit. It interprets a flavor
614 VM_REGION_BASIC_INFO as VM_REGION_BASIC_INFO_64, which is
615 dangerous since 'struct vm_region_basic_info_64' is larger than
616 'struct vm_region_basic_info'; therefore let's write
617 VM_REGION_BASIC_INFO_64 explicitly.
618 - mach_vm_region. It has arguments that are 64-bit always. This
619 function is useful when you want to access the VM of a process
620 other than the current process.
621 In 64-bit processes, we could use vm_region_64 or mach_vm_region.
622 I choose vm_region_64 because it uses the same types as vm_region,
623 resulting in less conditional code. */
624 # if defined __ppc64__ || defined __x86_64__
625 struct vm_region_basic_info_64 info;
626 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
628 more = (vm_region_64 (task, &address, &size, VM_REGION_BASIC_INFO_64,
629 (vm_region_info_t)&info, &info_count, &object_name)
632 struct vm_region_basic_info info;
633 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
635 more = (vm_region (task, &address, &size, VM_REGION_BASIC_INFO,
636 (vm_region_info_t)&info, &info_count, &object_name)
639 if (object_name != MACH_PORT_NULL)
640 mach_port_deallocate (mach_task_self (), object_name);
647 #elif (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__
648 /* Windows platform. Use the native Windows API. */
650 MEMORY_BASIC_INFORMATION info;
651 unsigned long address = 0;
652 unsigned long total = 0;
654 while (VirtualQuery ((void*)address, &info, sizeof(info)) == sizeof(info))
656 if (info.State != MEM_FREE)
657 /* Ignore areas where info.Protect has the undocumented value 0.
658 This is needed, so that on Cygwin, areas used by malloc() are
659 distinguished from areas reserved for future malloc(). */
660 if (info.Protect != 0)
661 total += info.RegionSize;
662 address = (unsigned long)info.BaseAddress + info.RegionSize;
666 #elif defined __BEOS__
667 /* Use the BeOS specific API. */
671 unsigned long total = 0;
674 while (get_next_area_info (0, &cookie, &info) == B_OK)
676 unsigned long start, end;
678 start = (unsigned long) info.address;
679 end = start + info.size;
681 total += end - start;
696 #if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* MacOS X, AIX, Cygwin */
697 /* get_rusage_as_via_setrlimit() does not work.
698 Prefer get_rusage_as_via_iterator(). */
699 return get_rusage_as_via_iterator ();
700 #elif HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT
701 /* Prefer get_rusage_as_via_setrlimit() if it succeeds,
702 because the caller may want to use the result with setrlimit(). */
705 result = get_rusage_as_via_setrlimit ();
707 result = get_rusage_as_via_iterator ();
710 return get_rusage_as_via_iterator ();