Mercurial > gnulib
annotate lib/get-rusage-as.c @ 17363:5a51fb7777a9
sys_select, sys_time: port 2013-01-30 Solaris 2.6 fix to Cygwin
Problem reported by Marco Atzeri in
<http://lists.gnu.org/archive/html/bug-gnulib/2013-03/msg00000.html>.
* lib/sys_select.in.h [HAVE_SYS_SELECT_H && _CYGWIN_SYS_TIME_H]:
Simply delegate to the system <sys/select.h> in this case too.
Also, pay attention to _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H only
if OSF/1, since otherwise Cygwin breaks, and it doesn't seem to
be needed on Solaris either.
* lib/sys_time.in.h [_CYGWIN_SYS_TIME_H]:
Simply delgate to the system <sys/time.h> in this case.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Tue, 19 Mar 2013 09:08:47 -0700 |
parents | e542fd46ad6f |
children | 344018b6e5d7 |
rev | line source |
---|---|
14175 | 1 /* Getter for RLIMIT_AS. |
17249
e542fd46ad6f
maint: update all copyright year number ranges
Eric Blake <eblake@redhat.com>
parents:
17185
diff
changeset
|
2 Copyright (C) 2011-2013 Free Software Foundation, Inc. |
14175 | 3 Written by Bruno Haible <bruno@clisp.org>, 2011. |
4 | |
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. | |
9 | |
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. | |
14 | |
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/>. */ | |
17 | |
18 #include <config.h> | |
19 | |
20 /* Specification. */ | |
21 #include "resource-ext.h" | |
22 | |
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. | |
30 | |
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 | |
38 platforms. | |
39 We define two functions | |
40 get_rusage_as_via_setrlimit(), | |
41 get_rusage_as_via_iterator(). | |
42 | |
43 Discussion per platform: | |
44 | |
45 Linux: | |
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(). | |
51 | |
16935
498a2211d839
Write "Mac OS X" instead of "MacOS X".
Bruno Haible <bruno@clisp.org>
parents:
16214
diff
changeset
|
52 Mac OS X: |
14175 | 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. | |
57 | |
58 FreeBSD: | |
59 a) setrlimit with RLIMIT_AS works. | |
60 b) The /proc/self/maps file contains a list of the virtual memory areas. | |
61 | |
62 NetBSD: | |
63 a) setrlimit with RLIMIT_AS works. | |
14218
5c7bf99f5907
get-rusage-as: Improve on NetBSD.
Bruno Haible <bruno@clisp.org>
parents:
14215
diff
changeset
|
64 b) The /proc/self/maps file contains a list of the virtual memory areas. |
5c7bf99f5907
get-rusage-as: Improve on NetBSD.
Bruno Haible <bruno@clisp.org>
parents:
14215
diff
changeset
|
65 Both methods agree, |
14175 | 66 |
67 OpenBSD: | |
68 a) setrlimit exists, but RLIMIT_AS is not defined. | |
14235
6b8b94f919ce
vma-iter, get-rusage-as: Add OpenBSD support.
Bruno Haible <bruno@clisp.org>
parents:
14227
diff
changeset
|
69 b) mquery() can be used to find out about the virtual memory areas. |
14175 | 70 |
71 AIX: | |
72 a) setrlimit with RLIMIT_AS succeeds but does not really work: The OS | |
73 apparently ignores RLIMIT_AS. mmap() of a page always succeeds, | |
74 therefore get_rusage_as_via_setrlimit() is always 0. | |
75 b) No VMA iteration API exists. | |
76 | |
77 HP-UX: | |
78 a) setrlimit with RLIMIT_AS works. | |
79 b) No VMA iteration API exists. | |
80 | |
81 IRIX: | |
82 a) setrlimit with RLIMIT_AS works. | |
83 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP. | |
84 Both methods agree, | |
85 | |
86 OSF/1: | |
87 a) setrlimit with RLIMIT_AS works. | |
88 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP. | |
89 The value returned by get_rusage_as_via_setrlimit() is 64 KB higher than | |
90 get_rusage_as_via_iterator(). It's not clear why. | |
91 | |
92 Solaris: | |
93 a) setrlimit with RLIMIT_AS works. | |
94 b) No VMA iteration API exists. | |
95 | |
96 Cygwin: | |
97 a) setrlimit with RLIMIT_AS always fails when the limit is < 0x80000000. | |
98 get_rusage_as_via_setrlimit() therefore produces a wrong value. | |
99 b) The /proc/$pid/maps file lists only the memory areas belonging to | |
100 the executable and shared libraries, not the anonymous memory. | |
16214
ec738d6aeef5
Talk about "native Windows API", not "Win32".
Bruno Haible <bruno@clisp.org>
parents:
16201
diff
changeset
|
101 But the native Windows API works. |
14175 | 102 |
103 mingw: | |
104 a) There is no setrlimit function. | |
16214
ec738d6aeef5
Talk about "native Windows API", not "Win32".
Bruno Haible <bruno@clisp.org>
parents:
16201
diff
changeset
|
105 b) The native Windows API works. |
14175 | 106 |
107 BeOS, Haiku: | |
108 a) On BeOS, there is no setrlimit function. | |
109 On Haiku, setrlimit exists. RLIMIT_AS is defined but unsupported. | |
110 b) There is a specific BeOS API: get_next_area_info(). | |
111 */ | |
112 | |
113 | |
114 #include <errno.h> /* errno */ | |
115 #include <stdlib.h> /* size_t, abort */ | |
116 #include <fcntl.h> /* open, O_RDONLY */ | |
117 #include <unistd.h> /* getpagesize, read, close */ | |
118 | |
119 | |
120 /* System support for get_rusage_as_via_setrlimit(). */ | |
121 | |
122 #if HAVE_SETRLIMIT | |
123 # include <sys/time.h> | |
124 # include <sys/resource.h> /* getrlimit, setrlimit */ | |
125 #endif | |
126 | |
127 /* Test whether mmap() and mprotect() are available. | |
128 We don't use HAVE_MMAP, because AC_FUNC_MMAP would not define it on HP-UX. | |
129 HAVE_MPROTECT is not enough, because mingw does not have mmap() but has an | |
130 mprotect() function in libgcc.a. */ | |
131 #if HAVE_SYS_MMAN_H && HAVE_MPROTECT | |
132 # include <fcntl.h> | |
133 # include <sys/types.h> | |
134 # include <sys/mman.h> /* mmap, munmap */ | |
135 /* Define MAP_FILE when it isn't otherwise. */ | |
136 # ifndef MAP_FILE | |
137 # define MAP_FILE 0 | |
138 # endif | |
139 #endif | |
140 | |
141 | |
142 /* System support for get_rusage_as_via_iterator(). */ | |
143 | |
14220 | 144 #include "vma-iter.h" |
14175 | 145 |
146 | |
147 #if HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT | |
148 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
149 static uintptr_t |
14175 | 150 get_rusage_as_via_setrlimit (void) |
151 { | |
152 uintptr_t result; | |
153 | |
154 struct rlimit orig_limit; | |
155 | |
156 # if HAVE_MAP_ANONYMOUS | |
157 const int flags = MAP_ANONYMOUS | MAP_PRIVATE; | |
158 const int fd = -1; | |
159 # else /* !HAVE_MAP_ANONYMOUS */ | |
160 const int flags = MAP_FILE | MAP_PRIVATE; | |
161 int fd = open ("/dev/zero", O_RDONLY, 0666); | |
162 if (fd < 0) | |
163 return 0; | |
164 # endif | |
165 | |
166 /* Record the original limit. */ | |
167 if (getrlimit (RLIMIT_AS, &orig_limit) < 0) | |
168 { | |
169 result = 0; | |
14215 | 170 goto done2; |
14175 | 171 } |
172 | |
173 if (orig_limit.rlim_max != RLIM_INFINITY | |
174 && (orig_limit.rlim_cur == RLIM_INFINITY | |
175 || orig_limit.rlim_cur > orig_limit.rlim_max)) | |
176 /* We may not be able to restore the current rlim_cur value. | |
177 So bail out. */ | |
178 { | |
179 result = 0; | |
14215 | 180 goto done2; |
14175 | 181 } |
182 | |
183 { | |
184 /* The granularity is a single page. */ | |
185 const size_t pagesize = getpagesize (); | |
186 | |
187 uintptr_t low_bound = 0; | |
188 uintptr_t high_bound; | |
189 | |
190 for (;;) | |
191 { | |
192 /* Here we know that the address space size is >= low_bound. */ | |
193 struct rlimit try_limit; | |
194 uintptr_t try_next = 2 * low_bound + pagesize; | |
195 | |
196 if (try_next < low_bound) | |
197 /* Overflow. */ | |
198 try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize; | |
199 | |
200 /* There's no point in trying a value > orig_limit.rlim_max, as | |
201 setrlimit would fail anyway. */ | |
202 if (orig_limit.rlim_max != RLIM_INFINITY | |
203 && orig_limit.rlim_max < try_next) | |
204 try_next = orig_limit.rlim_max; | |
205 | |
206 /* Avoid endless loop. */ | |
207 if (try_next == low_bound) | |
208 { | |
209 /* try_next could not be increased. */ | |
210 result = low_bound; | |
14215 | 211 goto done1; |
14175 | 212 } |
213 | |
214 try_limit.rlim_max = orig_limit.rlim_max; | |
215 try_limit.rlim_cur = try_next; | |
216 if (setrlimit (RLIMIT_AS, &try_limit) == 0) | |
217 { | |
218 /* Allocate a page of memory, to compare the current address space | |
219 size with try_limit.rlim_cur. */ | |
220 void *new_page = | |
221 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0); | |
222 | |
223 if (new_page != (void *)(-1)) | |
224 { | |
225 /* The page could be added successfully. Free it. */ | |
226 if (munmap (new_page, pagesize) < 0) | |
227 abort (); | |
228 /* We know that the address space size is | |
229 < try_limit.rlim_cur. */ | |
230 high_bound = try_next; | |
231 break; | |
232 } | |
233 else | |
234 { | |
235 /* We know that the address space size is | |
236 >= try_limit.rlim_cur. */ | |
237 low_bound = try_next; | |
238 } | |
239 } | |
240 else | |
241 { | |
242 /* Here we expect only EINVAL, not EPERM. */ | |
243 if (errno != EINVAL) | |
244 abort (); | |
245 /* We know that the address space size is | |
246 >= try_limit.rlim_cur. */ | |
247 low_bound = try_next; | |
248 } | |
249 } | |
250 | |
251 /* Here we know that the address space size is | |
252 >= low_bound and < high_bound. */ | |
253 while (high_bound - low_bound > pagesize) | |
254 { | |
255 struct rlimit try_limit; | |
256 uintptr_t try_next = | |
257 low_bound + (((high_bound - low_bound) / 2) / pagesize) * pagesize; | |
258 | |
259 /* Here low_bound <= try_next < high_bound. */ | |
260 try_limit.rlim_max = orig_limit.rlim_max; | |
261 try_limit.rlim_cur = try_next; | |
262 if (setrlimit (RLIMIT_AS, &try_limit) == 0) | |
263 { | |
264 /* Allocate a page of memory, to compare the current address space | |
265 size with try_limit.rlim_cur. */ | |
266 void *new_page = | |
267 mmap (NULL, pagesize, PROT_READ | PROT_WRITE, flags, fd, 0); | |
268 | |
269 if (new_page != (void *)(-1)) | |
270 { | |
271 /* The page could be added successfully. Free it. */ | |
272 if (munmap (new_page, pagesize) < 0) | |
273 abort (); | |
274 /* We know that the address space size is | |
275 < try_limit.rlim_cur. */ | |
276 high_bound = try_next; | |
277 } | |
278 else | |
279 { | |
280 /* We know that the address space size is | |
281 >= try_limit.rlim_cur. */ | |
282 low_bound = try_next; | |
283 } | |
284 } | |
285 else | |
286 { | |
287 /* Here we expect only EINVAL, not EPERM. */ | |
288 if (errno != EINVAL) | |
289 abort (); | |
290 /* We know that the address space size is | |
291 >= try_limit.rlim_cur. */ | |
292 low_bound = try_next; | |
293 } | |
294 } | |
295 | |
296 result = low_bound; | |
297 } | |
298 | |
14215 | 299 done1: |
14175 | 300 /* Restore the original rlim_cur value. */ |
301 if (setrlimit (RLIMIT_AS, &orig_limit) < 0) | |
302 abort (); | |
303 | |
14215 | 304 done2: |
14175 | 305 # if !HAVE_MAP_ANONYMOUS |
306 close (fd); | |
307 # endif | |
308 return result; | |
309 } | |
310 | |
14227
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
311 #else |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
312 |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
313 static uintptr_t |
14227
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
314 get_rusage_as_via_setrlimit (void) |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
315 { |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
316 return 0; |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
317 } |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
318 |
14175 | 319 #endif |
320 | |
321 | |
14220 | 322 #if VMA_ITERATE_SUPPORTED |
14175 | 323 |
14220 | 324 static int |
325 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end, | |
326 unsigned int flags) | |
327 { | |
328 uintptr_t *totalp = (uintptr_t *) data; | |
14175 | 329 |
14220 | 330 *totalp += end - start; |
14175 | 331 return 0; |
332 } | |
333 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
334 static uintptr_t |
14175 | 335 get_rusage_as_via_iterator (void) |
336 { | |
14220 | 337 uintptr_t total = 0; |
14175 | 338 |
14220 | 339 vma_iterate (vma_iterate_callback, &total); |
14175 | 340 |
341 return total; | |
14220 | 342 } |
14175 | 343 |
344 #else | |
345 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
346 static uintptr_t |
14220 | 347 get_rusage_as_via_iterator (void) |
348 { | |
14175 | 349 return 0; |
14220 | 350 } |
14175 | 351 |
352 #endif | |
353 | |
354 | |
355 uintptr_t | |
356 get_rusage_as (void) | |
357 { | |
16935
498a2211d839
Write "Mac OS X" instead of "MacOS X".
Bruno Haible <bruno@clisp.org>
parents:
16214
diff
changeset
|
358 #if (defined __APPLE__ && defined __MACH__) || defined _AIX || defined __CYGWIN__ /* Mac OS X, AIX, Cygwin */ |
14175 | 359 /* get_rusage_as_via_setrlimit() does not work. |
360 Prefer get_rusage_as_via_iterator(). */ | |
361 return get_rusage_as_via_iterator (); | |
362 #elif HAVE_SETRLIMIT && defined RLIMIT_AS && HAVE_SYS_MMAN_H && HAVE_MPROTECT | |
363 /* Prefer get_rusage_as_via_setrlimit() if it succeeds, | |
364 because the caller may want to use the result with setrlimit(). */ | |
365 uintptr_t result; | |
366 | |
367 result = get_rusage_as_via_setrlimit (); | |
368 if (result == 0) | |
369 result = get_rusage_as_via_iterator (); | |
370 return result; | |
371 #else | |
372 return get_rusage_as_via_iterator (); | |
373 #endif | |
374 } | |
14227
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
375 |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
376 |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
377 #ifdef TEST |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
378 |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
379 #include <stdio.h> |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
380 |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
381 int |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
382 main () |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
383 { |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
384 printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n", |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
385 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (), |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
386 get_rusage_as ()); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
387 malloc (0x88); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
388 printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n", |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
389 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (), |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
390 get_rusage_as ()); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
391 malloc (0x8812); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
392 printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n", |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
393 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (), |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
394 get_rusage_as ()); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
395 malloc (0x281237); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
396 printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n", |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
397 get_rusage_as_via_setrlimit (), get_rusage_as_via_iterator (), |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
398 get_rusage_as ()); |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
399 return 0; |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
400 } |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
401 |
8bb45aca6841
get-rusage-as: Allow for easier testing.
Bruno Haible <bruno@clisp.org>
parents:
14220
diff
changeset
|
402 #endif /* TEST */ |