Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -19,6 +19,7 @@ import json
|
|
| 19 |
import resource
|
| 20 |
import platform
|
| 21 |
from loguru import logger
|
|
|
|
| 22 |
|
| 23 |
app = FastAPI(title="Code Execution and Input Analysis API", docs_url="/")
|
| 24 |
|
|
@@ -62,6 +63,68 @@ class CodeExecutor:
|
|
| 62 |
"java": "javac",
|
| 63 |
"python": "python3",
|
| 64 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
def set_resource_limits(self):
|
| 67 |
"""Set resource limits for subprocess (Unix only)"""
|
|
@@ -72,6 +135,32 @@ class CodeExecutor:
|
|
| 72 |
resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY, MAX_MEMORY))
|
| 73 |
|
| 74 |
def check_available_memory(self) -> int:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
"""Check available memory in MB"""
|
| 76 |
try:
|
| 77 |
with open('/proc/meminfo', 'r') as f:
|
|
@@ -564,41 +653,38 @@ class CodeExecutor:
|
|
| 564 |
|
| 565 |
# Try different JVM configurations
|
| 566 |
jvm_configs = [
|
| 567 |
-
# Config 1:
|
| 568 |
[
|
| 569 |
-
"-
|
| 570 |
"-Xmx32m",
|
| 571 |
-
"-
|
| 572 |
-
"-XX:
|
| 573 |
-
"-XX:InitialCodeCacheSize=4m",
|
| 574 |
-
"-XX:MaxMetaspaceSize=32m",
|
| 575 |
-
"-XX:CompressedClassSpaceSize=8m",
|
| 576 |
-
"-XX:-TieredCompilation",
|
| 577 |
-
"-XX:CICompilerCount=1",
|
| 578 |
-
"-Xss256k",
|
| 579 |
-
"-XX:+UseStringDeduplication",
|
| 580 |
-
"-XX:MaxDirectMemorySize=16m",
|
| 581 |
"-Djava.awt.headless=true",
|
| 582 |
-
"-Dfile.encoding=UTF-8",
|
| 583 |
],
|
| 584 |
-
# Config 2:
|
| 585 |
[
|
| 586 |
-
"-
|
| 587 |
-
"-
|
|
|
|
| 588 |
"-XX:ReservedCodeCacheSize=8m",
|
| 589 |
-
"-XX:
|
|
|
|
| 590 |
"-XX:-TieredCompilation",
|
| 591 |
"-Djava.awt.headless=true",
|
| 592 |
],
|
| 593 |
-
# Config 3:
|
| 594 |
[
|
| 595 |
-
"-
|
| 596 |
-
"-
|
|
|
|
|
|
|
|
|
|
| 597 |
"-Djava.awt.headless=true",
|
| 598 |
],
|
| 599 |
-
# Config 4:
|
| 600 |
[
|
| 601 |
-
"-XX:+
|
|
|
|
| 602 |
"-XX:-TieredCompilation",
|
| 603 |
"-Djava.awt.headless=true",
|
| 604 |
],
|
|
@@ -610,7 +696,12 @@ class CodeExecutor:
|
|
| 610 |
logger.info(f"Trying JVM config {config_idx + 1} for {class_name}")
|
| 611 |
start_time = asyncio.get_event_loop().time()
|
| 612 |
|
| 613 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 614 |
|
| 615 |
stdout, stderr, returncode = await self._execute_with_input(
|
| 616 |
java_cmd, workspace, input_data
|
|
|
|
| 19 |
import resource
|
| 20 |
import platform
|
| 21 |
from loguru import logger
|
| 22 |
+
import subprocess
|
| 23 |
|
| 24 |
app = FastAPI(title="Code Execution and Input Analysis API", docs_url="/")
|
| 25 |
|
|
|
|
| 63 |
"java": "javac",
|
| 64 |
"python": "python3",
|
| 65 |
}
|
| 66 |
+
# Check and log ulimits on startup
|
| 67 |
+
self._check_ulimits()
|
| 68 |
+
|
| 69 |
+
def _check_ulimits(self):
|
| 70 |
+
"""Check current ulimits and log them"""
|
| 71 |
+
try:
|
| 72 |
+
# Check various ulimits
|
| 73 |
+
limits = {
|
| 74 |
+
"RLIMIT_NOFILE": resource.RLIMIT_NOFILE, # file descriptors
|
| 75 |
+
"RLIMIT_NPROC": resource.RLIMIT_NPROC, # processes
|
| 76 |
+
"RLIMIT_STACK": resource.RLIMIT_STACK, # stack size
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
logger.info("Current ulimits:")
|
| 80 |
+
for name, limit_type in limits.items():
|
| 81 |
+
try:
|
| 82 |
+
soft, hard = resource.getrlimit(limit_type)
|
| 83 |
+
logger.info(f"{name}: soft={soft}, hard={hard}")
|
| 84 |
+
except:
|
| 85 |
+
pass
|
| 86 |
+
|
| 87 |
+
# Try to increase limits if they're too low
|
| 88 |
+
try:
|
| 89 |
+
# Increase file descriptors
|
| 90 |
+
resource.setrlimit(resource.RLIMIT_NOFILE, (65536, 65536))
|
| 91 |
+
except:
|
| 92 |
+
try:
|
| 93 |
+
# Try a lower value if the high one fails
|
| 94 |
+
resource.setrlimit(resource.RLIMIT_NOFILE, (4096, 4096))
|
| 95 |
+
except:
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
logger.warning(f"Could not check/set ulimits: {e}")
|
| 100 |
+
|
| 101 |
+
async def _create_java_wrapper(self, workspace: str, jvm_opts: List[str]) -> str:
|
| 102 |
+
"""Create a wrapper script for Java execution with ulimit settings"""
|
| 103 |
+
|
| 104 |
+
wrapper_content = f"""#!/bin/bash
|
| 105 |
+
# Set ulimits to prevent memory allocation issues
|
| 106 |
+
ulimit -n 65536 2>/dev/null || ulimit -n 4096
|
| 107 |
+
ulimit -u 32768 2>/dev/null || ulimit -u 2048
|
| 108 |
+
ulimit -s 8192 2>/dev/null || ulimit -s 4096
|
| 109 |
+
ulimit -v unlimited 2>/dev/null || true
|
| 110 |
+
ulimit -m unlimited 2>/dev/null || true
|
| 111 |
+
|
| 112 |
+
# Log current ulimits for debugging
|
| 113 |
+
echo "Current ulimits:" >&2
|
| 114 |
+
ulimit -a >&2
|
| 115 |
+
|
| 116 |
+
# Execute Java with the provided options
|
| 117 |
+
exec java {' '.join(jvm_opts)} "$@"
|
| 118 |
+
"""
|
| 119 |
+
|
| 120 |
+
wrapper_path = os.path.join(workspace, "java_wrapper.sh")
|
| 121 |
+
with open(wrapper_path, 'w') as f:
|
| 122 |
+
f.write(wrapper_content)
|
| 123 |
+
|
| 124 |
+
# Make executable
|
| 125 |
+
os.chmod(wrapper_path, 0o755)
|
| 126 |
+
|
| 127 |
+
return wrapper_path
|
| 128 |
|
| 129 |
def set_resource_limits(self):
|
| 130 |
"""Set resource limits for subprocess (Unix only)"""
|
|
|
|
| 135 |
resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY, MAX_MEMORY))
|
| 136 |
|
| 137 |
def check_available_memory(self) -> int:
|
| 138 |
+
"""Create a wrapper script for Java execution with ulimit settings"""
|
| 139 |
+
|
| 140 |
+
wrapper_content = f"""#!/bin/bash
|
| 141 |
+
# Set ulimits to prevent memory allocation issues
|
| 142 |
+
ulimit -n 65536 2>/dev/null || ulimit -n 4096
|
| 143 |
+
ulimit -u 32768 2>/dev/null || ulimit -u 2048
|
| 144 |
+
ulimit -s 8192 2>/dev/null || ulimit -s 4096
|
| 145 |
+
ulimit -v unlimited 2>/dev/null || true
|
| 146 |
+
ulimit -m unlimited 2>/dev/null || true
|
| 147 |
+
|
| 148 |
+
# Log current ulimits for debugging
|
| 149 |
+
echo "Current ulimits:" >&2
|
| 150 |
+
ulimit -a >&2
|
| 151 |
+
|
| 152 |
+
# Execute Java with the provided options
|
| 153 |
+
exec java {' '.join(jvm_opts)} "$@"
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
wrapper_path = os.path.join(workspace, "java_wrapper.sh")
|
| 157 |
+
with open(wrapper_path, 'w') as f:
|
| 158 |
+
f.write(wrapper_content)
|
| 159 |
+
|
| 160 |
+
# Make executable
|
| 161 |
+
os.chmod(wrapper_path, 0o755)
|
| 162 |
+
|
| 163 |
+
return wrapper_path
|
| 164 |
"""Check available memory in MB"""
|
| 165 |
try:
|
| 166 |
with open('/proc/meminfo', 'r') as f:
|
|
|
|
| 653 |
|
| 654 |
# Try different JVM configurations
|
| 655 |
jvm_configs = [
|
| 656 |
+
# Config 1: Ultra minimal with no code cache reservation
|
| 657 |
[
|
| 658 |
+
"-Xint", # Interpreter only mode (no JIT)
|
| 659 |
"-Xmx32m",
|
| 660 |
+
"-Xms2m",
|
| 661 |
+
"-XX:MaxMetaspaceSize=16m",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
"-Djava.awt.headless=true",
|
|
|
|
| 663 |
],
|
| 664 |
+
# Config 2: Minimal with small code cache
|
| 665 |
[
|
| 666 |
+
"-XX:+UseSerialGC",
|
| 667 |
+
"-Xmx32m",
|
| 668 |
+
"-Xms4m",
|
| 669 |
"-XX:ReservedCodeCacheSize=8m",
|
| 670 |
+
"-XX:InitialCodeCacheSize=2m",
|
| 671 |
+
"-XX:MaxMetaspaceSize=24m",
|
| 672 |
"-XX:-TieredCompilation",
|
| 673 |
"-Djava.awt.headless=true",
|
| 674 |
],
|
| 675 |
+
# Config 3: Slightly more memory
|
| 676 |
[
|
| 677 |
+
"-Xmx48m",
|
| 678 |
+
"-Xms8m",
|
| 679 |
+
"-XX:ReservedCodeCacheSize=16m",
|
| 680 |
+
"-XX:MaxMetaspaceSize=32m",
|
| 681 |
+
"-XX:-TieredCompilation",
|
| 682 |
"-Djava.awt.headless=true",
|
| 683 |
],
|
| 684 |
+
# Config 4: Use container support
|
| 685 |
[
|
| 686 |
+
"-XX:+UseContainerSupport",
|
| 687 |
+
"-XX:MaxRAMPercentage=25.0",
|
| 688 |
"-XX:-TieredCompilation",
|
| 689 |
"-Djava.awt.headless=true",
|
| 690 |
],
|
|
|
|
| 696 |
logger.info(f"Trying JVM config {config_idx + 1} for {class_name}")
|
| 697 |
start_time = asyncio.get_event_loop().time()
|
| 698 |
|
| 699 |
+
# For first two configs, try with wrapper script
|
| 700 |
+
if config_idx < 2:
|
| 701 |
+
wrapper_path = await self._create_java_wrapper(workspace, jvm_opts)
|
| 702 |
+
java_cmd = [wrapper_path, class_name]
|
| 703 |
+
else:
|
| 704 |
+
java_cmd = ["java"] + jvm_opts + [class_name]
|
| 705 |
|
| 706 |
stdout, stderr, returncode = await self._execute_with_input(
|
| 707 |
java_cmd, workspace, input_data
|