using System.Diagnostics; using LiquidCode.Tester.Worker.Services.Isolate; namespace LiquidCode.Tester.Worker.Services; /// /// C++ program execution service using Isolate sandbox /// public class CppExecutionServiceIsolate : IExecutionService { private readonly ILogger _logger; private readonly IsolateService _isolateService; private readonly IsolateBoxPool _boxPool; public CppExecutionServiceIsolate( ILogger logger, IsolateService isolateService, IsolateBoxPool boxPool) { _logger = logger; _isolateService = isolateService; _boxPool = boxPool; } public async Task ExecuteAsync( string executablePath, string inputFilePath, int timeLimitMs, int memoryLimitMb) { _logger.LogInformation( "Executing {Executable} with Isolate: time={TimeLimit}ms, memory={MemoryLimit}MB", executablePath, timeLimitMs, memoryLimitMb); var result = new ExecutionResult(); var stopwatch = Stopwatch.StartNew(); int boxId = -1; try { // Acquire a box from pool boxId = await _boxPool.AcquireBoxAsync(); _logger.LogDebug("Acquired isolate box {BoxId}", boxId); // Initialize the box await _isolateService.InitBoxAsync(boxId); // Copy executable to box var boxDir = $"/var/local/lib/isolate/{boxId}/box"; var executableName = Path.GetFileName(executablePath); var boxExecutablePath = Path.Combine(boxDir, executableName); File.Copy(executablePath, boxExecutablePath, overwrite: true); // Make executable var chmodProcess = Process.Start(new ProcessStartInfo { FileName = "chmod", Arguments = $"+x {boxExecutablePath}", UseShellExecute = false }); chmodProcess?.WaitForExit(); // Prepare output file in box var outputFilePath = Path.Combine(boxDir, "output.txt"); // Run in Isolate var isolateResult = await _isolateService.RunAsync(new IsolateRunOptions { BoxId = boxId, Executable = $"/box/{executableName}", TimeLimitSeconds = timeLimitMs / 1000.0, WallTimeLimitSeconds = (timeLimitMs / 1000.0) * 2, MemoryLimitKb = memoryLimitMb * 1024, StackLimitKb = 256 * 1024, // 256 MB stack ProcessLimit = 1, // Single process only EnableNetwork = false, // No network access StdinFile = inputFilePath, StdoutFile = outputFilePath, WorkingDirectory = "/box" }); stopwatch.Stop(); // Read output if (File.Exists(outputFilePath)) { result.Output = await File.ReadAllTextAsync(outputFilePath); } // Map Isolate result to ExecutionResult result.ExecutionTimeMs = (long)(isolateResult.CpuTimeSeconds * 1000); result.MemoryUsedMb = isolateResult.MemoryUsedKb / 1024; result.ErrorOutput = isolateResult.ErrorOutput; result.ExitCode = isolateResult.ExitCode; if (isolateResult.TimeLimitExceeded) { result.TimeLimitExceeded = true; result.ErrorMessage = $"Time limit exceeded: {isolateResult.CpuTimeSeconds:F3}s"; _logger.LogWarning("Time limit exceeded for box {BoxId}", boxId); } else if (isolateResult.MemoryLimitExceeded) { result.MemoryLimitExceeded = true; result.ErrorMessage = $"Memory limit exceeded: {isolateResult.MemoryUsedKb / 1024}MB"; if (isolateResult.CgroupOomKilled) { result.ErrorMessage += " (OOM killed by cgroup)"; } _logger.LogWarning("Memory limit exceeded for box {BoxId}", boxId); } else if (isolateResult.RuntimeError) { result.RuntimeError = true; result.ErrorMessage = $"Runtime error: {isolateResult.Message}"; if (isolateResult.ExitSignal.HasValue) { result.ErrorMessage += $" (signal {isolateResult.ExitSignal})"; } _logger.LogWarning("Runtime error for box {BoxId}: {Message}", boxId, isolateResult.Message); } else if (isolateResult.ExitCode == 0) { result.Success = true; _logger.LogInformation( "Execution successful: time={Time}ms, memory={Memory}MB, box={BoxId}", result.ExecutionTimeMs, result.MemoryUsedMb, boxId); } else { result.RuntimeError = true; result.ErrorMessage = $"Non-zero exit code: {isolateResult.ExitCode}"; _logger.LogWarning("Non-zero exit code {ExitCode} for box {BoxId}", isolateResult.ExitCode, boxId); } // Log detailed statistics _logger.LogDebug( "Isolate stats: CPU={Cpu}s, Wall={Wall}s, Memory={Mem}KB, " + "VoluntaryContextSwitches={Vol}, ForcedContextSwitches={Forced}", isolateResult.CpuTimeSeconds, isolateResult.WallTimeSeconds, isolateResult.MemoryUsedKb, isolateResult.VoluntaryContextSwitches, isolateResult.ForcedContextSwitches); } catch (Exception ex) { stopwatch.Stop(); result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; result.RuntimeError = true; result.ErrorMessage = $"Execution error: {ex.Message}"; _logger.LogError(ex, "Error during Isolate execution"); } finally { // Cleanup box if (boxId >= 0) { try { await _isolateService.CleanupBoxAsync(boxId); _logger.LogDebug("Cleaned up isolate box {BoxId}", boxId); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to cleanup box {BoxId}", boxId); } // Release box back to pool _boxPool.ReleaseBox(boxId); _logger.LogDebug("Released box {BoxId} back to pool", boxId); } } return result; } }