Skip to content

Commit a7dca91

Browse files
authored
Use isolate's cg-oom-killed to detect "memory limit exceeded" conditions (#1471)
* Add separate "memory limit exceeded" message * make test suite check for memory limit exceeded * fix "memory limit exceeded" behavior in php PHP has a memory_limit setting that limits the amount of memory the interpreter even tries to allocate; this limit is 128M by default. Exceeding this causes PHP to exit with code 255 (and print a message on stdout). This commit changes the limit to infinity, and lets isolate's memory limit handle it instead. This gives the correct feedback to contestants.
1 parent 15a682c commit a7dca91

File tree

5 files changed

+24
-13
lines changed

5 files changed

+24
-13
lines changed

cms/grading/Sandbox.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class SandboxBase(metaclass=ABCMeta):
188188
EXIT_SIGNAL = 'signal'
189189
EXIT_TIMEOUT = 'timeout'
190190
EXIT_TIMEOUT_WALL = 'wall timeout'
191+
EXIT_MEM_LIMIT = 'memory limit exceeded'
191192
EXIT_NONZERO_RETURN = 'nonzero return'
192193

193194
def __init__(
@@ -1283,7 +1284,10 @@ def get_exit_status(self) -> str:
12831284
else:
12841285
return self.EXIT_TIMEOUT
12851286
elif 'SG' in status_list:
1286-
return self.EXIT_SIGNAL
1287+
if 'cg-oom-killed' in self.log:
1288+
return self.EXIT_MEM_LIMIT
1289+
else:
1290+
return self.EXIT_SIGNAL
12871291
elif 'RE' in status_list:
12881292
return self.EXIT_NONZERO_RETURN
12891293
# OK status is not reported in the log file, it's implicit.

cms/grading/languages/php.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ def get_evaluation_commands(
5959
self, executable_filename, main=None, args=None):
6060
"""See Language.get_evaluation_commands."""
6161
args = args if args is not None else []
62-
return [["/usr/bin/php", executable_filename] + args]
62+
return [["/usr/bin/php", "-d", "memory_limit=-1", executable_filename] + args]

cms/grading/steps/evaluation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,12 @@ def N_(message: str):
6767
"for example. Note that in this case the CPU time "
6868
"visible in the submission details might be much smaller "
6969
"than the time limit.")),
70+
HumanMessage("memorylimit",
71+
N_("Memory limit exceeded"),
72+
N_("Your submission used too much memory.")),
7073
HumanMessage("signal",
71-
N_("Execution killed (could be triggered by violating memory "
72-
"limits)"),
73-
N_("The evaluation was killed by a signal. "
74-
"Among other things, this might be caused by exceeding "
75-
"the memory limit. Note that if this is the reason, "
76-
"the memory usage visible in the submission details is "
77-
"the usage before the allocation that caused the "
78-
"signal.")),
74+
N_("Execution killed by signal"),
75+
N_("The evaluation was killed by a signal.")),
7976
HumanMessage("returncode",
8077
N_("Execution failed because the return code was nonzero"),
8178
N_("Your submission failed because it exited with a return "
@@ -244,6 +241,7 @@ def evaluation_step_after_run(
244241
Sandbox.EXIT_TIMEOUT,
245242
Sandbox.EXIT_TIMEOUT_WALL,
246243
Sandbox.EXIT_NONZERO_RETURN,
244+
Sandbox.EXIT_MEM_LIMIT,
247245
Sandbox.EXIT_SIGNAL]:
248246
# Evaluation succeeded, and user program was interrupted for some error
249247
# condition. We report the success, the task type should decide how to
@@ -288,6 +286,8 @@ def human_evaluation_message(stats: StatsDict) -> list[str]:
288286
elif exit_status == Sandbox.EXIT_SANDBOX_ERROR:
289287
# Contestants won't see this, the submission will still be evaluating.
290288
return []
289+
elif exit_status == Sandbox.EXIT_MEM_LIMIT:
290+
return [EVALUATION_MESSAGES.get("memorylimit").message]
291291
elif exit_status == Sandbox.EXIT_NONZERO_RETURN:
292292
# Don't tell which code: would be too much information!
293293
return [EVALUATION_MESSAGES.get("returncode").message]

cmstestsuite/Test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ def __init__(self):
136136
"Execution failed because the return code was nonzero")
137137

138138

139+
class CheckMemoryLimit(CheckAbstractEvaluationFailure):
140+
def __init__(self):
141+
CheckAbstractEvaluationFailure.__init__(
142+
self, "memory limit exceeded",
143+
"Memory limit exceeded")
144+
145+
139146
class CheckUserTest(ABC):
140147
@abstractmethod
141148
def check(self, *args, **kwargs):

cmstestsuite/Tests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import cmstestsuite.tasks.outputonly_comparator as outputonly_comparator
3838
import cmstestsuite.tasks.twosteps as twosteps
3939
import cmstestsuite.tasks.twosteps_comparator as twosteps_comparator
40-
from cmstestsuite.Test import Test, CheckOverallScore, CheckCompilationFail, \
40+
from cmstestsuite.Test import CheckMemoryLimit, Test, CheckOverallScore, CheckCompilationFail, \
4141
CheckTimeout, CheckTimeoutWall, CheckNonzeroReturn, CheckUserTestEvaluated
4242

4343

@@ -257,12 +257,12 @@
257257
task=batch_stdio, filenames=['oom-static.%l'],
258258
languages=(LANG_C, LANG_CPP, LANG_CPP14,
259259
LANG_CPP17, LANG_CPP20, LANG_PASCAL),
260-
checks=[CheckOverallScore(0, 100)]),
260+
checks=[CheckOverallScore(0, 100), CheckMemoryLimit()]),
261261

262262
Test('oom-heap',
263263
task=batch_stdio, filenames=['oom-heap.%l'],
264264
languages=ALL_LANGUAGES,
265-
checks=[CheckOverallScore(0, 100)]),
265+
checks=[CheckOverallScore(0, 100), CheckMemoryLimit()]),
266266

267267
# Tasks with graders.
268268

0 commit comments

Comments
 (0)