Skip to content

Commit ea8b090

Browse files
committed
[GR-54688] Implement getrusage with PosixSupportLibrary to support RUSAGE_CHILDREN
1 parent 6e74923 commit ea8b090

File tree

16 files changed

+237
-142
lines changed

16 files changed

+237
-142
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_resource.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -38,16 +38,10 @@
3838
# SOFTWARE.
3939

4040

41-
def assert_raises(err, fn, *args, **kwargs):
42-
raised = False
43-
try:
44-
fn(*args, **kwargs)
45-
except err:
46-
raised = True
47-
assert raised
48-
49-
5041
def test_import():
42+
import sys
43+
if sys.platform not in ['darwin', 'linux']:
44+
return
5145
imported = True
5246
try:
5347
import resource
@@ -57,7 +51,11 @@ def test_import():
5751

5852

5953
def test_gerusage():
60-
from resource import getrusage, RUSAGE_SELF, RUSAGE_THREAD
54+
from resource import getrusage, RUSAGE_SELF
55+
try:
56+
from resource import RUSAGE_THREAD
57+
except ImportError:
58+
RUSAGE_THREAD = RUSAGE_SELF
6159
for who in [RUSAGE_SELF, RUSAGE_THREAD]:
6260
ru = getrusage(who)
6361
attrs = [

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_resource.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*graalpython.lib-python.3.test.test_resource.ResourceTest.test_freebsd_contants
2+
*graalpython.lib-python.3.test.test_resource.ResourceTest.test_getrusage
23
*graalpython.lib-python.3.test.test_resource.ResourceTest.test_linux_constants
34
*graalpython.lib-python.3.test.test_resource.ResourceTest.test_pagesize
45
*graalpython.lib-python.3.test.test_resource.ResourceTest.test_prlimit

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ResourceModuleBuiltins.java

Lines changed: 37 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -42,42 +42,40 @@
4242

4343
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
4444

45-
import java.lang.management.ManagementFactory;
46-
import java.lang.management.MemoryMXBean;
47-
import java.lang.management.MemoryUsage;
48-
import java.lang.management.ThreadMXBean;
4945
import java.util.List;
5046

51-
import org.graalvm.nativeimage.ImageInfo;
52-
5347
import com.oracle.graal.python.builtins.Builtin;
5448
import com.oracle.graal.python.builtins.CoreFunctions;
5549
import com.oracle.graal.python.builtins.Python3Core;
5650
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
5751
import com.oracle.graal.python.builtins.PythonBuiltins;
58-
import com.oracle.graal.python.builtins.objects.thread.PThread;
52+
import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum;
5953
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
6054
import com.oracle.graal.python.builtins.objects.tuple.StructSequence;
6155
import com.oracle.graal.python.nodes.ErrorMessages;
56+
import com.oracle.graal.python.nodes.PConstructAndRaiseNode;
6257
import com.oracle.graal.python.nodes.PRaiseNode;
6358
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
6459
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
60+
import com.oracle.graal.python.runtime.PosixConstants;
61+
import com.oracle.graal.python.runtime.PosixSupport;
62+
import com.oracle.graal.python.runtime.PosixSupportLibrary;
63+
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
64+
import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult;
6565
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
66-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
66+
import com.oracle.truffle.api.dsl.Bind;
6767
import com.oracle.truffle.api.dsl.Cached;
68-
import com.oracle.truffle.api.dsl.Fallback;
6968
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
7069
import com.oracle.truffle.api.dsl.ImportStatic;
7170
import com.oracle.truffle.api.dsl.NodeFactory;
7271
import com.oracle.truffle.api.dsl.Specialization;
72+
import com.oracle.truffle.api.frame.VirtualFrame;
73+
import com.oracle.truffle.api.library.CachedLibrary;
74+
import com.oracle.truffle.api.nodes.Node;
7375

7476
@CoreFunctions(defineModule = "resource")
7577
public final class ResourceModuleBuiltins extends PythonBuiltins {
7678

77-
static int RUSAGE_CHILDREN = -1;
78-
static int RUSAGE_SELF = 0;
79-
static int RUSAGE_THREAD = 1;
80-
8179
static int RLIMIT_CPU = 0;
8280
static int RLIMIT_FSIZE = 1;
8381
static int RLIMIT_DATA = 2;
@@ -129,9 +127,13 @@ public void initialize(Python3Core core) {
129127

130128
addBuiltinConstant("error", PythonBuiltinClassType.OSError);
131129

132-
addBuiltinConstant("RUSAGE_CHILDREN", RUSAGE_CHILDREN);
133-
addBuiltinConstant("RUSAGE_SELF", RUSAGE_SELF);
134-
addBuiltinConstant("RUSAGE_THREAD", RUSAGE_THREAD);
130+
if (PosixConstants.RUSAGE_CHILDREN.defined) {
131+
addBuiltinConstant("RUSAGE_CHILDREN", PosixConstants.RUSAGE_CHILDREN.getValueIfDefined());
132+
}
133+
addBuiltinConstant("RUSAGE_SELF", PosixConstants.RUSAGE_SELF.value);
134+
if (PosixConstants.RUSAGE_THREAD.defined) {
135+
addBuiltinConstant("RUSAGE_THREAD", PosixConstants.RUSAGE_THREAD.getValueIfDefined());
136+
}
135137

136138
addBuiltinConstant("RLIMIT_CPU", RLIMIT_CPU);
137139
addBuiltinConstant("RLIMIT_FSIZE", RLIMIT_FSIZE);
@@ -154,119 +156,29 @@ public void initialize(Python3Core core) {
154156
@ImportStatic(ResourceModuleBuiltins.class)
155157
abstract static class GetRuUsageNode extends PythonBuiltinNode {
156158

157-
@Specialization(guards = {"who == RUSAGE_THREAD"})
158-
@TruffleBoundary
159-
PTuple getruusageThread(@SuppressWarnings("unused") int who) {
160-
long id = PThread.getThreadId(Thread.currentThread());
161-
Runtime runtime = Runtime.getRuntime();
162-
163-
double ru_utime = 0; // time in user mode (float)
164-
double ru_stime = 0; // time in system mode (float)
165-
long ru_maxrss; // maximum resident set size
166-
167-
if (!ImageInfo.inImageCode()) {
168-
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
169-
if (threadMXBean.isCurrentThreadCpuTimeSupported()) {
170-
ru_utime = threadMXBean.getThreadUserTime(id) / 1000000000.0;
171-
ru_stime = Math.max(0, (threadMXBean.getThreadCpuTime(id) - threadMXBean.getThreadUserTime(id))) / 1000000000.0;
172-
}
173-
174-
if (threadMXBean instanceof com.sun.management.ThreadMXBean) {
175-
com.sun.management.ThreadMXBean thMxBean = (com.sun.management.ThreadMXBean) threadMXBean;
176-
ru_maxrss = thMxBean.getThreadAllocatedBytes(id);
159+
@Specialization
160+
static PTuple getruusage(VirtualFrame frame, int who,
161+
@Bind("this") Node inliningTarget,
162+
@CachedLibrary(limit = "1") PosixSupportLibrary posixLib,
163+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode,
164+
@Cached PRaiseNode.Lazy raiseNode) {
165+
PosixSupport posixSupport = PosixSupport.get(inliningTarget);
166+
RusageResult rusage;
167+
try {
168+
rusage = posixLib.getrusage(posixSupport, who);
169+
} catch (PosixException e) {
170+
if (e.getErrorCode() == OSErrorEnum.EINVAL.getNumber()) {
171+
throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.RUSAGE_INVALID_WHO);
177172
} else {
178-
ru_maxrss = runtime.maxMemory();
173+
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);
179174
}
180-
} else {
181-
ru_maxrss = runtime.maxMemory();
182-
}
183-
184-
String osName = System.getProperty("os.name");
185-
if (osName.contains("Linux")) {
186-
// peak memory usage (kilobytes on Linux
187-
ru_maxrss /= 1024;
188175
}
189176

190-
long ru_ixrss = -1; // shared memory size
191-
long ru_idrss = -1; // unshared memory size
192-
long ru_isrss = -1; // unshared stack size
193-
long ru_minflt = -1; // page faults not requiring I/O
194-
long ru_majflt = -1; // page faults requiring I/O
195-
long ru_nswap = -1; // number of swap outs
196-
long ru_inblock = -1; // block input operations
197-
long ru_oublock = -1; // block output operations
198-
long ru_msgsnd = -1; // messages sent
199-
long ru_msgrcv = -1; // messages received
200-
long ru_nsignals = -1; // signals received
201-
long ru_nvcsw = -1; // voluntary context switches
202-
long ru_nivcsw = -1; // nvoluntary context switches
203-
return PythonObjectFactory.getUncached().createStructSeq(STRUCT_RUSAGE_DESC, ru_utime, ru_stime, ru_maxrss, ru_ixrss, ru_idrss, ru_isrss,
204-
ru_minflt, ru_majflt, ru_nswap, ru_inblock, ru_oublock, ru_msgsnd, ru_msgrcv, ru_nsignals,
205-
ru_nvcsw, ru_nivcsw);
206-
}
207-
208-
@Specialization(guards = {"who == RUSAGE_SELF"})
209-
@TruffleBoundary
210-
PTuple getruusageSelf(@SuppressWarnings("unused") int who) {
211-
Runtime runtime = Runtime.getRuntime();
212-
213-
double ru_utime = 0; // time in user mode (float)
214-
double ru_stime = 0; // time in system mode (float)
215-
long ru_maxrss;
216-
217-
if (!ImageInfo.inImageCode()) {
218-
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
219-
if (threadMXBean.isThreadCpuTimeSupported()) {
220-
for (long thId : threadMXBean.getAllThreadIds()) {
221-
long tu = threadMXBean.getThreadUserTime(thId);
222-
long tc = threadMXBean.getThreadCpuTime(thId);
223-
224-
if (tu != -1) {
225-
ru_utime += tu / 1000000000.0;
226-
}
227-
228-
if (tu != -1 && tc != -1) {
229-
ru_stime += Math.max(0, tc - tu) / 1000000000.0;
230-
}
231-
}
232-
}
233-
234-
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
235-
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
236-
MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
237-
ru_maxrss = heapMemoryUsage.getCommitted() + nonHeapMemoryUsage.getCommitted();
238-
} else {
239-
ru_maxrss = runtime.maxMemory();
240-
}
241-
242-
String osName = System.getProperty("os.name");
243-
if (osName.contains("Linux")) {
244-
// peak memory usage (kilobytes on Linux
245-
ru_maxrss /= 1024;
246-
}
247-
248-
long ru_ixrss = -1; // shared memory size
249-
long ru_idrss = -1; // unshared memory size
250-
long ru_isrss = -1; // unshared stack size
251-
long ru_minflt = -1; // page faults not requiring I/O
252-
long ru_majflt = -1; // page faults requiring I/O
253-
long ru_nswap = -1; // number of swap outs
254-
long ru_inblock = -1; // block input operations
255-
long ru_oublock = -1; // block output operations
256-
long ru_msgsnd = -1; // messages sent
257-
long ru_msgrcv = -1; // messages received
258-
long ru_nsignals = -1; // signals received
259-
long ru_nvcsw = -1; // voluntary context switches
260-
long ru_nivcsw = -1; // nvoluntary context switches
261-
return PythonObjectFactory.getUncached().createStructSeq(STRUCT_RUSAGE_DESC, ru_utime, ru_stime, ru_maxrss, ru_ixrss, ru_idrss, ru_isrss,
262-
ru_minflt, ru_majflt, ru_nswap, ru_inblock, ru_oublock, ru_msgsnd, ru_msgrcv, ru_nsignals,
263-
ru_nvcsw, ru_nivcsw);
264-
}
265-
266-
@Fallback
267-
static PTuple getruusage(@SuppressWarnings("unused") Object who,
268-
@Cached PRaiseNode raiseNode) {
269-
throw raiseNode.raise(ValueError, ErrorMessages.RUSAGE_NOT_YET_IMPLEMENED);
177+
return PythonObjectFactory.getUncached().createStructSeq(STRUCT_RUSAGE_DESC,
178+
rusage.ru_utime(), rusage.ru_stime(),
179+
rusage.ru_maxrss(), rusage.ru_ixrss(), rusage.ru_idrss(), rusage.ru_isrss(),
180+
rusage.ru_minflt(), rusage.ru_majflt(), rusage.ru_nswap(), rusage.ru_inblock(), rusage.ru_oublock(),
181+
rusage.ru_msgsnd(), rusage.ru_msgrcv(), rusage.ru_nsignals(), rusage.ru_nvcsw(), rusage.ru_nivcsw());
270182
}
271183
}
272184

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ public abstract class ErrorMessages {
845845
public static final TruffleString GETPWUID_NOT_FOUND = tsLiteral("getpwuid(): uid not found");
846846
public static final TruffleString EXPECTED_INT_MESSAGE = tsLiteral("Expected an int as second argument to ldexp.");
847847
public static final TruffleString NOT_IMPLEMENTED = tsLiteral("not implemented");
848-
public static final TruffleString RUSAGE_NOT_YET_IMPLEMENED = tsLiteral("rusage usage not yet implemented for specified arg.");
848+
public static final TruffleString RUSAGE_INVALID_WHO = tsLiteral("invalid who parameter");
849849
public static final TruffleString SOCKADDR_RESOLVED_TO_MULTIPLE_ADDRESSES = tsLiteral("sockaddr resolved to multiple addresses");
850850
public static final TruffleString IPV4_MUST_BE_2_TUPLE = tsLiteral("IPv4 sockaddr must be 2 tuple");
851851
public static final TruffleString GETNAMEINFO_ARG1_MUST_BE_TUPLE = tsLiteral("getnameinfo() argument 1 must be a tuple");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
package com.oracle.graal.python.runtime;
4242

4343
import static com.oracle.graal.python.builtins.PythonOS.PLATFORM_WIN32;
44+
import static com.oracle.graal.python.builtins.PythonOS.PLATFORM_LINUX;
4445
import static com.oracle.graal.python.builtins.PythonOS.getPythonOS;
46+
import static com.oracle.graal.python.builtins.objects.thread.PThread.getThreadId;
4547
import static com.oracle.graal.python.nodes.BuiltinNames.T__SIGNAL;
4648
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
4749
import static com.oracle.graal.python.nodes.StringLiterals.T_JAVA;
@@ -235,6 +237,7 @@
235237
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
236238
import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult;
237239
import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult;
240+
import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult;
238241
import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult;
239242
import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval;
240243
import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr;
@@ -2125,6 +2128,70 @@ public long[] getgroups() {
21252128
throw new UnsupportedPosixFeatureException("getgroups was excluded");
21262129
}
21272130

2131+
@ExportMessage
2132+
@TruffleBoundary
2133+
public RusageResult getrusage(int who) throws PosixException {
2134+
Runtime runtime = Runtime.getRuntime();
2135+
double ru_utime = 0; // time in user mode (float)
2136+
double ru_stime = 0; // time in system mode (float)
2137+
long ru_maxrss;
2138+
2139+
if (!ImageInfo.inImageCode()) {
2140+
// once GR-44559 is fixed we can enable this branch on NI
2141+
java.lang.management.ThreadMXBean threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean();
2142+
if (PosixConstants.RUSAGE_THREAD.defined &&
2143+
who == PosixConstants.RUSAGE_THREAD.getValueIfDefined()) {
2144+
long id = getThreadId(Thread.currentThread());
2145+
if (threadMXBean.isCurrentThreadCpuTimeSupported()) {
2146+
ru_utime = threadMXBean.getThreadUserTime(id) / 1000000000.0;
2147+
ru_stime = Math.max(0, (threadMXBean.getThreadCpuTime(id) - threadMXBean.getThreadUserTime(id))) / 1000000000.0;
2148+
}
2149+
2150+
if (threadMXBean instanceof com.sun.management.ThreadMXBean) {
2151+
com.sun.management.ThreadMXBean thMxBean = (com.sun.management.ThreadMXBean) threadMXBean;
2152+
ru_maxrss = thMxBean.getThreadAllocatedBytes(id);
2153+
} else {
2154+
ru_maxrss = runtime.maxMemory();
2155+
}
2156+
} else if (who == PosixConstants.RUSAGE_SELF.value) {
2157+
if (threadMXBean.isThreadCpuTimeSupported()) {
2158+
for (long thId : threadMXBean.getAllThreadIds()) {
2159+
long tu = threadMXBean.getThreadUserTime(thId);
2160+
long tc = threadMXBean.getThreadCpuTime(thId);
2161+
2162+
if (tu != -1) {
2163+
ru_utime += tu / 1000000000.0;
2164+
}
2165+
2166+
if (tu != -1 && tc != -1) {
2167+
ru_stime += Math.max(0, tc - tu) / 1000000000.0;
2168+
}
2169+
}
2170+
}
2171+
2172+
java.lang.management.MemoryMXBean memoryMXBean = java.lang.management.ManagementFactory.getMemoryMXBean();
2173+
java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
2174+
java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
2175+
ru_maxrss = heapMemoryUsage.getCommitted() + nonHeapMemoryUsage.getCommitted();
2176+
} else {
2177+
throw posixException(OSErrorEnum.EINVAL);
2178+
}
2179+
} else if (who == PosixConstants.RUSAGE_SELF.value ||
2180+
(PosixConstants.RUSAGE_THREAD.defined && who == PosixConstants.RUSAGE_THREAD.getValueIfDefined())) {
2181+
ru_maxrss = runtime.maxMemory();
2182+
} else {
2183+
throw posixException(OSErrorEnum.EINVAL);
2184+
}
2185+
2186+
if (PythonOS.getPythonOS() == PLATFORM_LINUX) {
2187+
// peak memory usage (kilobytes on Linux)
2188+
ru_maxrss /= 1024;
2189+
}
2190+
2191+
return new RusageResult(ru_utime, ru_stime, ru_maxrss,
2192+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
2193+
}
2194+
21282195
@ExportMessage
21292196
@SuppressWarnings("static-method")
21302197
public OpenPtyResult openpty() {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ImageBuildtimePosixSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException;
6060
import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult;
6161
import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult;
62+
import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult;
6263
import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult;
6364
import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval;
6465
import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr;
@@ -726,6 +727,13 @@ final long[] getgroups(
726727
return nativeLib.getgroups(nativePosixSupport);
727728
}
728729

730+
@ExportMessage
731+
final RusageResult getrusage(int who,
732+
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
733+
checkNotInImageBuildtime();
734+
return nativeLib.getrusage(nativePosixSupport, who);
735+
}
736+
729737
@ExportMessage
730738
final OpenPtyResult openpty(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
731739
checkNotInImageBuildtime();

0 commit comments

Comments
 (0)