1 /* resource.cc: getrusage () and friends.
3 Written by Steve Chamberlain (sac@cygnus.com), Doug Evans (dje@cygnus.com),
4 Geoffrey Noer (noer@cygnus.com) of Cygnus Support.
5 Rewritten by Sergey S. Okhapkin (sos@prospect.com.ru)
7 This file is part of Cygwin.
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
15 #include <sys/param.h>
23 #include "shared_info.h"
26 /* add timeval values */
28 add_timeval (struct timeval
*tv1
, struct timeval
*tv2
)
30 tv1
->tv_sec
+= tv2
->tv_sec
;
31 tv1
->tv_usec
+= tv2
->tv_usec
;
32 if (tv1
->tv_usec
>= USPERSEC
)
34 tv1
->tv_usec
-= USPERSEC
;
39 /* add rusage values of r2 to r1 */
41 add_rusage (struct rusage
*r1
, struct rusage
*r2
)
43 add_timeval (&r1
->ru_utime
, &r2
->ru_utime
);
44 add_timeval (&r1
->ru_stime
, &r2
->ru_stime
);
45 r1
->ru_maxrss
+= r2
->ru_maxrss
;
46 r1
->ru_ixrss
+= r2
->ru_ixrss
;
47 r1
->ru_idrss
+= r2
->ru_idrss
;
48 r1
->ru_isrss
+= r2
->ru_isrss
;
49 r1
->ru_minflt
+= r2
->ru_minflt
;
50 r1
->ru_majflt
+= r2
->ru_majflt
;
51 r1
->ru_nswap
+= r2
->ru_nswap
;
52 r1
->ru_inblock
+= r2
->ru_inblock
;
53 r1
->ru_oublock
+= r2
->ru_oublock
;
54 r1
->ru_msgsnd
+= r2
->ru_msgsnd
;
55 r1
->ru_msgrcv
+= r2
->ru_msgrcv
;
56 r1
->ru_nsignals
+= r2
->ru_nsignals
;
57 r1
->ru_nvcsw
+= r2
->ru_nvcsw
;
58 r1
->ru_nivcsw
+= r2
->ru_nivcsw
;
61 /* FIXME: what about other fields? */
63 fill_rusage (struct rusage
*r
, HANDLE h
)
65 KERNEL_USER_TIMES kut
;
69 memset (&kut
, 0, sizeof kut
);
70 memset (r
, 0, sizeof *r
);
71 NtQueryInformationProcess (h
, ProcessTimes
, &kut
, sizeof kut
, NULL
);
72 totimeval (&tv
, &kut
.KernelTime
, 0, 0);
73 add_timeval (&r
->ru_stime
, &tv
);
74 totimeval (&tv
, &kut
.UserTime
, 0, 0);
75 add_timeval (&r
->ru_utime
, &tv
);
78 NTSTATUS status
= NtQueryInformationProcess (h
, ProcessVmCounters
, &vmc
,
80 if (NT_SUCCESS (status
))
82 r
->ru_maxrss
+= (long) (vmc
.WorkingSetSize
/ 1024);
83 r
->ru_majflt
+= vmc
.PageFaultCount
;
88 getrusage (int intwho
, struct rusage
*rusage_in
)
93 if (intwho
== RUSAGE_SELF
)
95 memset (&r
, 0, sizeof (r
));
96 fill_rusage (&r
, GetCurrentProcess ());
99 else if (intwho
== RUSAGE_CHILDREN
)
100 *rusage_in
= myself
->rusage_children
;
107 syscall_printf ("%R = getrusage(%d, %p)", res
, intwho
, rusage_in
);
111 /* Default stacksize in case RLIMIT_STACK is RLIM_INFINITY is 2 Megs with
112 system-dependent number of guard pages. The pthread stacksize does not
113 include the guardpage size, so we have to subtract the default guardpage
114 size. Additionally the Windows stack handling disallows to commit the
115 last page, so we subtract it, too. */
116 #define DEFAULT_STACKSIZE (2 * 1024 * 1024)
117 #define DEFAULT_STACKGUARD (wincap.def_guard_page_size() + wincap.page_size ())
119 muto NO_COPY rlimit_stack_guard
;
120 static struct rlimit rlimit_stack
= { 0, RLIM_INFINITY
};
123 __set_rlimit_stack (const struct rlimit
*rlp
)
125 rlimit_stack_guard
.init ("rlimit_stack_guard")->acquire ();
127 rlimit_stack_guard
.release ();
131 __get_rlimit_stack (struct rlimit
*rlp
)
133 rlimit_stack_guard
.init ("rlimit_stack_guard")->acquire ();
134 if (!rlimit_stack
.rlim_cur
)
136 /* Fetch the default stacksize from the executable header... */
137 PIMAGE_DOS_HEADER dosheader
;
138 PIMAGE_NT_HEADERS ntheader
;
140 dosheader
= (PIMAGE_DOS_HEADER
) GetModuleHandle (NULL
);
141 ntheader
= (PIMAGE_NT_HEADERS
) ((PBYTE
) dosheader
+ dosheader
->e_lfanew
);
142 rlimit_stack
.rlim_cur
= ntheader
->OptionalHeader
.SizeOfStackReserve
;
143 /* ...and subtract the guardpages. */
144 rlimit_stack
.rlim_cur
-= DEFAULT_STACKGUARD
;
147 rlimit_stack_guard
.release ();
150 /* Interface to stack limit called from pthread_create and
151 pthread_attr_getstacksize. */
153 get_rlimit_stack (void)
157 __get_rlimit_stack (&rl
);
158 /* RLIM_INFINITY doesn't make much sense. As in glibc, use an
159 "architecture-specific default". */
160 if (rl
.rlim_cur
== RLIM_INFINITY
)
161 rl
.rlim_cur
= DEFAULT_STACKSIZE
- DEFAULT_STACKGUARD
;
162 /* Always return at least minimum stacksize. */
163 else if (rl
.rlim_cur
< PTHREAD_STACK_MIN
)
164 rl
.rlim_cur
= PTHREAD_STACK_MIN
;
165 return (size_t) rl
.rlim_cur
;
168 static LONG job_serial_number
__attribute__((section (".cygwin_dll_common"), shared
));
171 job_shared_name (PWCHAR buf
, LONG num
)
173 __small_swprintf (buf
, L
"rlimit.%d", num
);
178 __get_rlimit_as (struct rlimit
*rlp
)
180 UNICODE_STRING uname
;
182 OBJECT_ATTRIBUTES attr
;
185 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo
;
187 if (cygheap
->rlim_as_id
)
189 RtlInitUnicodeString (&uname
,
190 job_shared_name (jobname
,
191 cygheap
->rlim_as_id
));
192 InitializeObjectAttributes (&attr
, &uname
, 0,
193 get_session_parent_dir (), NULL
);
194 /* May fail, just check NULL job in that case. */
195 NtOpenJobObject (&job
, JOB_OBJECT_QUERY
, &attr
);
197 status
= NtQueryInformationJobObject (job
,
198 JobObjectExtendedLimitInformation
,
199 &jobinfo
, sizeof jobinfo
, NULL
);
200 if (NT_SUCCESS (status
)
201 && (jobinfo
.BasicLimitInformation
.LimitFlags
202 & JOB_OBJECT_LIMIT_PROCESS_MEMORY
))
203 rlp
->rlim_cur
= rlp
->rlim_max
= jobinfo
.ProcessMemoryLimit
;
209 __set_rlimit_as (unsigned long new_as_limit
)
212 UNICODE_STRING uname
;
214 OBJECT_ATTRIBUTES attr
;
215 NTSTATUS status
= STATUS_SUCCESS
;
217 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo
= { 0 };
219 /* If we already have a limit, we must not change it because that
220 would potentially influence already running child processes.
221 Just try to create another, nested job. */
222 while (new_as_id
== 0)
223 new_as_id
= InterlockedIncrement (&job_serial_number
);
224 RtlInitUnicodeString (&uname
,
225 job_shared_name (jobname
, new_as_id
));
226 InitializeObjectAttributes (&attr
, &uname
, 0,
227 get_session_parent_dir (), NULL
);
228 status
= NtCreateJobObject (&job
, JOB_OBJECT_ALL_ACCESS
, &attr
);
229 if (!NT_SUCCESS (status
))
231 __seterrno_from_nt_status (status
);
234 jobinfo
.BasicLimitInformation
.LimitFlags
235 = JOB_OBJECT_LIMIT_PROCESS_MEMORY
;
236 /* Per Linux man page, round down to system pagesize. */
237 jobinfo
.ProcessMemoryLimit
238 = rounddown (new_as_limit
, wincap
.allocation_granularity ());
239 status
= NtSetInformationJobObject (job
,
240 JobObjectExtendedLimitInformation
,
241 &jobinfo
, sizeof jobinfo
);
242 /* If creating the job and setting up the job limits succeeded,
243 try to add the process to the job. This must be the last step,
244 otherwise we couldn't remove the job if anything failed. */
245 if (NT_SUCCESS (status
))
246 status
= NtAssignProcessToJobObject (job
, NtCurrentProcess ());
248 if (!NT_SUCCESS (status
))
250 __seterrno_from_nt_status (status
);
253 cygheap
->rlim_as_id
= new_as_id
;
258 getrlimit (int resource
, struct rlimit
*rlp
)
262 rlp
->rlim_cur
= RLIM_INFINITY
;
263 rlp
->rlim_max
= RLIM_INFINITY
;
272 __get_rlimit_as (rlp
);
275 __get_rlimit_stack (rlp
);
278 rlp
->rlim_cur
= rlp
->rlim_max
= OPEN_MAX
;
281 rlp
->rlim_cur
= cygheap
->rlim_core
;
295 setrlimit (int resource
, const struct rlimit
*rlp
)
297 struct rlimit oldlimits
;
299 /* Check if the request is to actually change the resource settings.
300 If it does not result in a change, take no action and do not fail. */
301 if (getrlimit (resource
, &oldlimits
) < 0)
306 if (oldlimits
.rlim_cur
== rlp
->rlim_cur
&&
307 oldlimits
.rlim_max
== rlp
->rlim_max
)
308 /* No change in resource requirements, succeed immediately */
311 if (rlp
->rlim_cur
> rlp
->rlim_max
)
317 if (rlp
->rlim_cur
> oldlimits
.rlim_max
)
326 if (rlp
->rlim_cur
!= RLIM_INFINITY
)
327 return __set_rlimit_as (rlp
->rlim_cur
);
330 cygheap
->rlim_core
= rlp
->rlim_cur
;
333 if (rlp
->rlim_cur
!= RLIM_INFINITY
)
334 return setdtablesize (rlp
->rlim_cur
);
337 __set_rlimit_stack (rlp
);