Mercurial > hg > octave-lojdl > gnulib-hg
annotate lib/get-rusage-data.c @ 17325:9d7698fd5b5f
openpty: fix bug where HAVE_OPENPTY is mistakenly 1
Problem reported by Mats Erik Andersson in
<http://lists.gnu.org/archive/html/bug-gnulib/2013-02/msg00051.html>.
* m4/pty.m4 (gl_FUNC_OPENPTY): Define HAVE_OPENPTY when the
openpty function exists, not merely when we intend to replace it.
This corrects the 2013-01-31 patch, which mistakenly defined
HAVE_OPENPTY even on hosts that lacked it.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Fri, 08 Feb 2013 08:03:23 -0800 |
parents | e542fd46ad6f |
children |
rev | line source |
---|---|
14228 | 1 /* Getter for RLIMIT_DATA. |
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. |
14228 | 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 "data segment size" is defined as the virtual memory area of the | |
24 current process that contains malloc()ed memory. | |
25 | |
26 There are two ways of retrieving the current data segment size: | |
27 a) by trying setrlimit with various values and observing whether the | |
28 kernel allows additional sbrk() calls, | |
29 b) by using system dependent APIs that allow to iterate over the list | |
30 of virtual memory areas. | |
31 We define two functions | |
32 get_rusage_data_via_setrlimit(), | |
33 get_rusage_data_via_iterator(). | |
34 | |
35 The variant | |
36 a') by trying setrlimit with various values and observing whether | |
37 additional malloc() calls succeed | |
38 is not as good as a), because a malloc() call can be served by already | |
39 allocated memory or through mmap(), and because a malloc() of 1 page may | |
40 require 2 pages. | |
41 | |
42 Discussion per platform: | |
43 | |
44 Linux: | |
45 a) setrlimit with RLIMIT_DATA works. | |
46 b) The /proc/self/maps file contains a list of the virtual memory areas. | |
47 get_rusage_data_via_setrlimit() returns the sum of the length of the | |
48 executable's data segment plus the heap VMA (an anonymous memory area), | |
49 whereas get_rusage_data_via_iterator() returns only the latter. | |
50 Note that malloc() falls back on mmap() for large allocations and also | |
51 for small allocations if there is not enough room in the data segment. | |
52 | |
16935
498a2211d839
Write "Mac OS X" instead of "MacOS X".
Bruno Haible <bruno@clisp.org>
parents:
16214
diff
changeset
|
53 Mac OS X: |
14228 | 54 a) setrlimit with RLIMIT_DATA succeeds but does not really work: The OS |
55 ignores RLIMIT_DATA. Therefore get_rusage_data_via_setrlimit() is | |
56 always 0. | |
57 b) The Mach based API works. | |
58 Note that malloc() falls back on mmap() for large allocations. | |
59 | |
60 FreeBSD: | |
61 a) setrlimit with RLIMIT_DATA works. | |
62 b) The /proc/self/maps file contains a list of the virtual memory areas. | |
63 | |
64 NetBSD: | |
65 a) setrlimit with RLIMIT_DATA works. | |
66 b) The /proc/self/maps file contains a list of the virtual memory areas. | |
67 Both methods agree. | |
68 Note that malloc() uses mmap() for large allocations. | |
69 | |
70 OpenBSD: | |
71 a) setrlimit with RLIMIT_DATA works. | |
14235
6b8b94f919ce
vma-iter, get-rusage-as: Add OpenBSD support.
Bruno Haible <bruno@clisp.org>
parents:
14228
diff
changeset
|
72 b) mquery() can be used to find out about the virtual memory areas. |
6b8b94f919ce
vma-iter, get-rusage-as: Add OpenBSD support.
Bruno Haible <bruno@clisp.org>
parents:
14228
diff
changeset
|
73 get_rusage_data_via_setrlimit() works much better than |
6b8b94f919ce
vma-iter, get-rusage-as: Add OpenBSD support.
Bruno Haible <bruno@clisp.org>
parents:
14228
diff
changeset
|
74 get_rusage_data_via_iterator(). |
14228 | 75 Note that malloc() appears to use mmap() for both large and small |
76 allocations. | |
77 | |
78 AIX: | |
79 a) setrlimit with RLIMIT_DATA works. | |
80 b) No VMA iteration API exists. | |
81 | |
82 HP-UX: | |
83 a) setrlimit with RLIMIT_DATA works, except on HP-UX 11.00, where it | |
84 cannot restore the previous limits, and except on HP-UX 11.11, where | |
85 it sometimes has no effect. | |
86 b) No VMA iteration API exists. | |
87 | |
88 IRIX: | |
89 a) setrlimit with RLIMIT_DATA works. | |
90 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP. | |
91 get_rusage_data_via_setrlimit() works slightly better than | |
92 get_rusage_data_via_iterator() before the first malloc() call. | |
93 | |
94 OSF/1: | |
95 a) setrlimit with RLIMIT_DATA works. | |
96 b) The /proc/$pid file supports ioctls PIOCNMAP and PIOCMAP. | |
97 Both methods agree. | |
98 | |
99 Solaris: | |
100 a) setrlimit with RLIMIT_DATA works. | |
101 b) No VMA iteration API exists. | |
102 | |
103 Cygwin: | |
104 a) setrlimit with RLIMIT_DATA always fails. | |
105 get_rusage_data_via_setrlimit() therefore produces a wrong value. | |
106 b) The /proc/$pid/maps file lists only the memory areas belonging to | |
107 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
|
108 But the native Windows API works. |
14228 | 109 Note that malloc() apparently falls back on mmap() for large allocations. |
110 | |
111 mingw: | |
112 a) There is no setrlimit function. | |
113 b) There is no sbrk() function. | |
114 Note that malloc() falls back on VirtualAlloc() for large allocations. | |
115 | |
116 BeOS, Haiku: | |
117 a) On BeOS, there is no setrlimit function. | |
118 On Haiku, setrlimit exists. RLIMIT_DATA is defined but unsupported: | |
119 getrlimit of RLIMIT_DATA always fails with errno = EINVAL. | |
120 b) There is a specific BeOS API: get_next_area_info(). | |
121 */ | |
122 | |
123 | |
124 #include <errno.h> /* errno */ | |
125 #include <stdlib.h> /* size_t, abort, malloc, free, sbrk */ | |
126 #include <fcntl.h> /* open, O_RDONLY */ | |
127 #include <unistd.h> /* getpagesize, read, close */ | |
128 | |
129 | |
130 /* System support for get_rusage_data_via_setrlimit(). */ | |
131 | |
132 #if HAVE_SETRLIMIT | |
133 # include <sys/time.h> | |
134 # include <sys/resource.h> /* getrlimit, setrlimit */ | |
135 # include <sys/utsname.h> | |
136 # include <string.h> /* strlen, strcmp */ | |
137 #endif | |
138 | |
139 | |
140 /* System support for get_rusage_data_via_iterator(). */ | |
141 | |
142 #include "vma-iter.h" | |
143 | |
144 | |
145 #if HAVE_SETRLIMIT && defined RLIMIT_DATA | |
146 | |
147 # ifdef _AIX | |
148 # define errno_expected() (errno == EINVAL || errno == EFAULT) | |
149 # else | |
150 # define errno_expected() (errno == EINVAL) | |
151 # endif | |
152 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
153 static uintptr_t |
14228 | 154 get_rusage_data_via_setrlimit (void) |
155 { | |
156 uintptr_t result; | |
157 | |
158 struct rlimit orig_limit; | |
159 | |
160 # ifdef __hpux | |
161 /* On HP-UX 11.00, setrlimit() RLIMIT_DATA of does not work: It cannot | |
162 restore the previous limits. | |
163 On HP-UX 11.11, setrlimit() RLIMIT_DATA of does not work: It sometimes | |
164 has no effect on the next sbrk() call. */ | |
165 { | |
166 struct utsname buf; | |
167 | |
168 if (uname (&buf) == 0 | |
169 && strlen (buf.release) >= 5 | |
170 && (strcmp (buf.release + strlen (buf.release) - 5, "11.00") == 0 | |
171 || strcmp (buf.release + strlen (buf.release) - 5, "11.11") == 0)) | |
172 return 0; | |
173 } | |
174 # endif | |
175 | |
176 /* Record the original limit. */ | |
177 if (getrlimit (RLIMIT_DATA, &orig_limit) < 0) | |
178 return 0; | |
179 | |
180 if (orig_limit.rlim_max != RLIM_INFINITY | |
181 && (orig_limit.rlim_cur == RLIM_INFINITY | |
182 || orig_limit.rlim_cur > orig_limit.rlim_max)) | |
183 /* We may not be able to restore the current rlim_cur value. | |
184 So bail out. */ | |
185 return 0; | |
186 | |
187 { | |
188 /* The granularity is a single page. */ | |
189 const intptr_t pagesize = getpagesize (); | |
190 | |
191 uintptr_t low_bound = 0; | |
192 uintptr_t high_bound; | |
193 | |
194 for (;;) | |
195 { | |
196 /* Here we know that the data segment size is >= low_bound. */ | |
197 struct rlimit try_limit; | |
198 uintptr_t try_next = 2 * low_bound + pagesize; | |
199 | |
200 if (try_next < low_bound) | |
201 /* Overflow. */ | |
202 try_next = ((uintptr_t) (~ 0) / pagesize) * pagesize; | |
203 | |
204 /* There's no point in trying a value > orig_limit.rlim_max, as | |
205 setrlimit would fail anyway. */ | |
206 if (orig_limit.rlim_max != RLIM_INFINITY | |
207 && orig_limit.rlim_max < try_next) | |
208 try_next = orig_limit.rlim_max; | |
209 | |
210 /* Avoid endless loop. */ | |
211 if (try_next == low_bound) | |
212 { | |
213 /* try_next could not be increased. */ | |
214 result = low_bound; | |
215 goto done; | |
216 } | |
217 | |
218 try_limit.rlim_max = orig_limit.rlim_max; | |
219 try_limit.rlim_cur = try_next; | |
220 if (setrlimit (RLIMIT_DATA, &try_limit) == 0) | |
221 { | |
222 /* Allocate a page of memory, to compare the current data segment | |
223 size with try_limit.rlim_cur. */ | |
224 void *new_page = sbrk (pagesize); | |
225 | |
226 if (new_page != (void *)(-1)) | |
227 { | |
228 /* The page could be added successfully. Free it. */ | |
229 sbrk (- pagesize); | |
230 /* We know that the data segment size is | |
231 < try_limit.rlim_cur. */ | |
232 high_bound = try_next; | |
233 break; | |
234 } | |
235 else | |
236 { | |
237 /* We know that the data segment size is | |
238 >= try_limit.rlim_cur. */ | |
239 low_bound = try_next; | |
240 } | |
241 } | |
242 else | |
243 { | |
244 /* Here we expect only EINVAL or (on AIX) EFAULT, not EPERM. */ | |
245 if (! errno_expected ()) | |
246 abort (); | |
247 /* We know that the data segment size is | |
248 >= try_limit.rlim_cur. */ | |
249 low_bound = try_next; | |
250 } | |
251 } | |
252 | |
253 /* Here we know that the data segment size is | |
254 >= low_bound and < high_bound. */ | |
255 while (high_bound - low_bound > pagesize) | |
256 { | |
257 struct rlimit try_limit; | |
258 uintptr_t try_next = | |
259 low_bound + (((high_bound - low_bound) / 2) / pagesize) * pagesize; | |
260 | |
261 /* Here low_bound <= try_next < high_bound. */ | |
262 try_limit.rlim_max = orig_limit.rlim_max; | |
263 try_limit.rlim_cur = try_next; | |
264 if (setrlimit (RLIMIT_DATA, &try_limit) == 0) | |
265 { | |
266 /* Allocate a page of memory, to compare the current data segment | |
267 size with try_limit.rlim_cur. */ | |
268 void *new_page = sbrk (pagesize); | |
269 | |
270 if (new_page != (void *)(-1)) | |
271 { | |
272 /* The page could be added successfully. Free it. */ | |
273 sbrk (- pagesize); | |
274 /* We know that the data segment size is | |
275 < try_limit.rlim_cur. */ | |
276 high_bound = try_next; | |
277 } | |
278 else | |
279 { | |
280 /* We know that the data segment size is | |
281 >= try_limit.rlim_cur. */ | |
282 low_bound = try_next; | |
283 } | |
284 } | |
285 else | |
286 { | |
287 /* Here we expect only EINVAL or (on AIX) EFAULT, not EPERM. */ | |
288 if (! errno_expected ()) | |
289 abort (); | |
290 /* We know that the data segment size is | |
291 >= try_limit.rlim_cur. */ | |
292 low_bound = try_next; | |
293 } | |
294 } | |
295 | |
296 result = low_bound; | |
297 } | |
298 | |
299 done: | |
300 /* Restore the original rlim_cur value. */ | |
301 if (setrlimit (RLIMIT_DATA, &orig_limit) < 0) | |
302 abort (); | |
303 | |
304 return result; | |
305 } | |
306 | |
307 #else | |
308 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
309 static uintptr_t |
14228 | 310 get_rusage_data_via_setrlimit (void) |
311 { | |
312 return 0; | |
313 } | |
314 | |
315 #endif | |
316 | |
317 | |
318 #if VMA_ITERATE_SUPPORTED | |
319 | |
320 struct locals | |
321 { | |
322 uintptr_t brk_value; | |
323 uintptr_t data_segment_size; | |
324 }; | |
325 | |
326 static int | |
327 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end, | |
328 unsigned int flags) | |
329 { | |
330 struct locals *lp = (struct locals *) data; | |
331 | |
332 if (start <= lp->brk_value && lp->brk_value - 1 <= end - 1) | |
333 { | |
334 lp->data_segment_size = end - start; | |
335 return 1; | |
336 } | |
337 return 0; | |
338 } | |
339 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
340 static uintptr_t |
14228 | 341 get_rusage_data_via_iterator (void) |
342 { | |
343 # if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __BEOS__ || defined __HAIKU__ | |
16214
ec738d6aeef5
Talk about "native Windows API", not "Win32".
Bruno Haible <bruno@clisp.org>
parents:
16201
diff
changeset
|
344 /* On native Windows, there is no sbrk() function. |
14228 | 345 On Haiku, sbrk(0) always returns 0. */ |
346 static void *brk_value; | |
347 | |
348 if (brk_value == NULL) | |
349 { | |
350 brk_value = malloc (1); | |
351 if (brk_value == NULL) | |
352 return 0; | |
353 } | |
354 # else | |
355 void *brk_value; | |
356 | |
357 brk_value = sbrk (0); | |
358 if (brk_value == (void *)-1) | |
359 return 0; | |
360 # endif | |
361 | |
362 { | |
363 struct locals l; | |
364 | |
365 l.brk_value = (uintptr_t) brk_value; | |
366 l.data_segment_size = 0; | |
367 vma_iterate (vma_iterate_callback, &l); | |
368 | |
369 return l.data_segment_size; | |
370 } | |
371 } | |
372 | |
373 #else | |
374 | |
17185
dd46d4e6beea
dup, execute, fatal-signal, etc.: no 'static inline'
Paul Eggert <eggert@cs.ucla.edu>
parents:
16935
diff
changeset
|
375 static uintptr_t |
14228 | 376 get_rusage_data_via_iterator (void) |
377 { | |
378 return 0; | |
379 } | |
380 | |
381 #endif | |
382 | |
383 | |
384 uintptr_t | |
385 get_rusage_data (void) | |
386 { | |
16935
498a2211d839
Write "Mac OS X" instead of "MacOS X".
Bruno Haible <bruno@clisp.org>
parents:
16214
diff
changeset
|
387 #if (defined __APPLE__ && defined __MACH__) || defined __CYGWIN__ /* Mac OS X, Cygwin */ |
14228 | 388 /* get_rusage_data_via_setrlimit() does not work. |
389 Prefer get_rusage_data_via_iterator(). */ | |
390 return get_rusage_data_via_iterator (); | |
391 #elif HAVE_SETRLIMIT && defined RLIMIT_DATA | |
392 # if defined __linux__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined _AIX || defined __sgi || defined __osf__ || defined __sun /* Linux, FreeBSD, NetBSD, OpenBSD, AIX, IRIX, OSF/1, Solaris */ | |
393 /* get_rusage_data_via_setrlimit() works. */ | |
394 return get_rusage_data_via_setrlimit (); | |
395 # else | |
396 /* Prefer get_rusage_data_via_setrlimit() if it succeeds, | |
397 because the caller may want to use the result with setrlimit(). */ | |
398 uintptr_t result; | |
399 | |
400 result = get_rusage_data_via_setrlimit (); | |
401 if (result == 0) | |
402 result = get_rusage_data_via_iterator (); | |
403 return result; | |
404 # endif | |
405 #else | |
406 return get_rusage_data_via_iterator (); | |
407 #endif | |
408 } | |
409 | |
410 | |
411 #ifdef TEST | |
412 | |
413 #include <stdio.h> | |
414 | |
415 int | |
416 main () | |
417 { | |
418 printf ("Initially: 0x%08lX 0x%08lX 0x%08lX\n", | |
419 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (), | |
420 get_rusage_data ()); | |
421 malloc (0x88); | |
422 printf ("After small malloc: 0x%08lX 0x%08lX 0x%08lX\n", | |
423 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (), | |
424 get_rusage_data ()); | |
425 malloc (0x8812); | |
426 printf ("After medium malloc: 0x%08lX 0x%08lX 0x%08lX\n", | |
427 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (), | |
428 get_rusage_data ()); | |
429 malloc (0x281237); | |
430 printf ("After large malloc: 0x%08lX 0x%08lX 0x%08lX\n", | |
431 get_rusage_data_via_setrlimit (), get_rusage_data_via_iterator (), | |
432 get_rusage_data ()); | |
433 return 0; | |
434 } | |
435 | |
436 #endif /* TEST */ |