Skip to content

Commit ca5f410

Browse files
committed
support profile module
1 parent 0215104 commit ca5f410

File tree

8 files changed

+435
-53
lines changed

8 files changed

+435
-53
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.test.runtime;
42+
43+
import org.junit.Before;
44+
import org.junit.Test;
45+
46+
import com.oracle.graal.python.test.PythonTests;
47+
48+
import java.io.ByteArrayOutputStream;
49+
import java.io.PrintStream;
50+
51+
import static org.junit.Assert.assertEquals;
52+
53+
public class ProfileTests {
54+
55+
@Before
56+
public void ensureBytecode() {
57+
PythonTests.skipOnLegacyASTInterpreter();
58+
}
59+
60+
@Test
61+
public void profileYield() {
62+
String source = "import sys\n" +
63+
"def f(frame, event, arg): print(frame, event, arg)\n" +
64+
"def fg(): yield 42\n" +
65+
"g = fg()\n" +
66+
"sys.setprofile(f)\n" +
67+
"for i in g: pass";
68+
assertPrints("fg> call None\n" +
69+
"fg> return 42\n" +
70+
"fg> call None\n" +
71+
"fg> return None\n" +
72+
"<module>> return None\n", source);
73+
}
74+
75+
@Test
76+
public void profileException() {
77+
String source = "import sys\n" +
78+
"def f(frame, event, arg): print(frame, event, arg)\n" +
79+
"sys.setprofile(f)\n" +
80+
"try:\n" +
81+
" max(0)\n" +
82+
"except Exception:\n" +
83+
" pass\n";
84+
assertPrints("<module>> c_call <built-in function max>\n" +
85+
"<module>> c_exception <built-in function max>\n" +
86+
"<module>> return None\n", source);
87+
}
88+
89+
private static void assertPrints(String expected, String code) {
90+
final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
91+
final PrintStream printStream = new PrintStream(byteArray);
92+
PythonTests.runScript(new String[0], code, printStream, System.err);
93+
String result = byteArray.toString().replaceAll("\r\n", "\n");
94+
assertEquals(expected, result.replaceAll(".*code ", ""));
95+
}
96+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*graalpython.lib-python.3.test.test_profile.ProfileTest.test_calling_conventions
2+
*graalpython.lib-python.3.test.test_profile.ProfileTest.test_cprofile
3+
*graalpython.lib-python.3.test.test_profile.ProfileTest.test_run
4+
*graalpython.lib-python.3.test.test_profile.ProfileTest.test_run_profile_as_module
5+
*graalpython.lib-python.3.test.test_profile.ProfileTest.test_runctx
6+
*graalpython.lib-python.3.test.test_profile.TestMain.test_main

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,10 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
213213
private static final LanguageReference<PythonLanguage> REFERENCE = LanguageReference.create(PythonLanguage.class);
214214

215215
/**
216-
* This assumption will be valid if no context set a trace function at any point. Calling
217-
* sys.settrace(None) will not invalidate it
216+
* This assumption will be valid if no context set a trace or profile function at any point.
217+
* Calling sys.settrace(None) or sys.setprofile(None) will not invalidate it
218218
*/
219-
public final Assumption noTracingAssumption = Assumption.create("No tracing function was set");
219+
public final Assumption noTracingOrProfilingAssumption = Assumption.create("No tracing function was set");
220220

221221
@CompilationFinal private boolean singleContext = true;
222222

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ public void postInitialize0(Python3Core core) {
539539

540540
if (context.getOption(PythonOptions.EnableBytecodeInterpreter)) {
541541
sys.setAttribute(tsLiteral("settrace"), sys.getAttribute(tsLiteral("_settrace")));
542+
sys.setAttribute(tsLiteral("setprofile"), sys.getAttribute(tsLiteral("_setprofile")));
542543
}
543544

544545
TruffleString coreHome = context.getCoreHome();
@@ -1010,6 +1011,27 @@ Object settrace(Object function) {
10101011
}
10111012
}
10121013

1014+
@Builtin(name = "_setprofile", minNumOfPositionalArgs = 1, parameterNames = {
1015+
"function"}, doc = "Set the profiling function. It will be called on each function call\nand return. See the profiler chapter in the library manual.")
1016+
@GenerateNodeFactory
1017+
abstract static class SetProfile extends PythonBuiltinNode {
1018+
@Specialization
1019+
Object settrace(Object function) {
1020+
PythonContext ctx = getContext();
1021+
if (!ctx.getOption(PythonOptions.EnableBytecodeInterpreter)) {
1022+
throw raise(NotImplementedError, ErrorMessages.SETPROFILE_NOT_IMPLEMENTED);
1023+
}
1024+
PythonLanguage language = getLanguage();
1025+
PythonContext.PythonThreadState state = ctx.getThreadState(language);
1026+
if (function == PNone.NONE) {
1027+
state.setProfileFun(null, language);
1028+
} else {
1029+
state.setProfileFun(function, language);
1030+
}
1031+
return PNone.NONE;
1032+
}
1033+
}
1034+
10131035
@Builtin(name = "gettrace")
10141036
@GenerateNodeFactory
10151037
abstract static class GetTrace extends PythonBuiltinNode {
@@ -1023,6 +1045,18 @@ Object gettrace() {
10231045
}
10241046
}
10251047

1048+
@Builtin(name = "getprofile")
1049+
@GenerateNodeFactory
1050+
abstract static class GetProfile extends PythonBuiltinNode {
1051+
@Specialization
1052+
Object getProfile() {
1053+
PythonContext ctx = getContext();
1054+
PythonContext.PythonThreadState state = ctx.getThreadState(getLanguage());
1055+
Object trace = state.getProfileFun();
1056+
return trace == null ? PNone.NONE : trace;
1057+
}
1058+
}
1059+
10261060
@Builtin(name = J_UNRAISABLEHOOK, minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 2, declaresExplicitSelf = true, doc = "unraisablehook($module, unraisable, /)\n" +
10271061
"--\n" +
10281062
"\n" +

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,7 @@ public abstract class ErrorMessages {
14571457
public static final TruffleString HPY_DEBUG_MODE_NOT_AVAILABLE = tsLiteral("HPy debug mode is not available");
14581458

14591459
public static final TruffleString SETTRACE_NOT_IMPLEMENTED = tsLiteral("sys.settrace is only implemented for the bytecode interpreter.");
1460+
public static final TruffleString SETPROFILE_NOT_IMPLEMENTED = tsLiteral("sys.setprofile is only implemented for the bytecode interpreter.");
14601461
public static final TruffleString ATTRIBUTE_VALUE_MUST_BE_BOOL = tsLiteral("attribute value type must be bool");
14611462
public static final TruffleString HPY_UNEXPECTED_HPY_NULL = tsLiteral("unexpected HPy_NULL");
14621463
public static final TruffleString TOKEN_ALREADY_USED = tsLiteral("%s has already been used once");

0 commit comments

Comments
 (0)