Judge Tasks
Running Judge Programs

Running Judge Programs

Seele provides two action tasks for performing judge-related tasks. They are used by specifying seele/run-judge/compile@1 and seele/run-judge/run@1 for the action. The former is mainly for compiling some source files and saving the generated files, and the latter is for running the program to be evaluated.

For convenience, we use "compilation task" to refer to the former and "execution task" to refer to the latter in this document.

Compilation tasks and execution tasks build sandboxes using Linux container technology to ensure that the programs to be run are executed in an isolated environment. This not only prevents interference between different judge tasks but also prevents malicious programs from damaging the system while limiting the resources the program can use. To properly use compilation tasks and execution tasks, you may need to master some knowledge about Linux containers.

Compilation Task

The compilation task is usually combined with the Add File task, adding the files from the latter to the container for the compilation program to use. After the compilation program runs successfully, the compilation task will copy the specified files from the container to the root folder.

Parameter List

The parameters accepted by the compilation task are as follows:

NameTypeDefaultDescription
sourcesstring[][]List of files to be mounted from the root folder to the container
savesstring[][]List of files to be saved from the container when the task is successful
cacheobjectSee belowCache configuration
Other attributesContainerConfigSee belowOther container configurations

cache Property

Seele supports caching for compilation tasks through the cache property. Its parameters are as follows:

NameTypeDefaultDescription
enabledbooleanfalseWhether to enable caching functionality
extrastring[][]List of additional parameters to be included in the hash calculation for caching

The compilation task calculates the SHA-256 hash value using the values listed below in order to determine whether the cache is hit. When the cache is hit, the compilation task will skip running the container and directly reuse the files specified in saves from the cache.

  1. The value of command.
  2. Each string in extra.
  3. Each string in saves, sorted in dictionary order.
  4. Each string in sources, sorted in dictionary order.
  5. The content of the file pointed to by each string in sources.

Example

The example below adds a main.c file to the root folder in the prepare step, mounts this main.c file to the container and runs the gcc program in the container for compilation in the compile step, and finally saves the output main program to the root folder for use in subsequent steps.

steps:
  prepare:
    action: "seele/add-file@1"
    files:
      - path: "main.c"
        plain: |
          #include <stdio.h>
          int main(void) {
            printf("Hello, world!\n");
            return 0;
          }
 
  compile:
    action: "seele/run-judge/compile@1"
    image: "gcc:11-bullseye"
    command: "gcc -O2 -Wall main.c -o main"
    sources: ["main.c"]
    saves: ["main"]

Execution Tasks

Execution tasks are generally used to run programs that need to be judged. Users often need to limit the runtime, memory usage, etc., of the programs being judged.

Parameter List

The parameter list accepted by the execution task is shown in the table below:

NameTypeDefaultDescription
filesstring[][]List of files mounted from the root folder to the container
Other propertiesContainerConfigSee belowOther container configurations

For mounting executable files, we recommend adding the :exec suffix to the file names in files so that Seele ensures that the executable file has execute permissions.

Example

The example below adds a main executable file to the root folder in the prepare step, and then runs the program in the run step.

steps:
  prepare:
    action: "seele/add-file@1"
    files:
      - path: "main"
        url: "http://darkyzhou.net/main"
 
  compile:
    action: "seele/run-judge/run@1"
    image: "gcc:11-bullseye"
    command: "main"
    files: ["main:exec"]

Common Configurations

ContainerConfig

Some parameters shared by compilation tasks and execution tasks for building sandboxes are as follows:

NameTypeDefaultDescription
imagestringN/AContainer image to be used
cwdstring[]Current directory when running programs in the container
commandstring or string[]N/AProgram to be run in the container
fdobjectnullConfiguration for input and output streams of the running program
pathsstring[][]Additional PATH environment variable items provided for the container running the program
mountsstring[] or object[][]List of files mounted from the root folder to the container
limitsobjectSee belowSome resource limits set for the container
⚠️

When a property has a default value of N/A, you must provide a value for it, otherwise Seele may not be able to parse the judge task.

Seele parses the container image specified in image in a similar way to Docker. Below are some valid examples of image values: gcc, debian:slim, library/ubuntu:focal, quay.io/foo/bar:latest

fd Configuration

The fd configuration can redirect the standard input, output, and error streams of the judge program to files in the root folder. Its parameters are as follows:

NameTypeDescription
stdinstringRedirect the program's standard input stream from the given file
stdoutstringRedirect the program's standard output stream to the given file, the sandbox will automatically create the file
stderrstringRedirect the program's standard error stream to the given file, the sandbox will automatically create the file
stdout_to_stderrbooleanRedirect the program's standard output stream to the standard error stream
stderr_to_stdoutbooleanRedirect the program's standard error stream to the standard output stream

If the user does not set a redirection relationship for a certain stream, the sandbox will redirect the stream to the /dev/null provided by the Linux kernel.

limits Configuration

The limits configuration can limit the resources used by the judge program. The sandbox will terminate the program when it uses resources beyond the limit.

Its parameters are as follows:

NameTypeDefaultDescription
time_msnumber10sUser-mode CPU time limit. Unit: ms
memory_kibnumber256 MiBMemory usage limit. Unit: KiB
pids_countnumber32Number of child processes the program can create
fsize_kibnumber64 MiBMaximum output data size the program can produce

The sandbox starts an additional timer with a time of time_ms * 3 after launching the program. If the program execution has not ended when the timer expires, the sandbox will terminate the program with the SIGKILL signal.

⚠️

Do not set memory_kib to a value lower than 20 MiB. Due to the principle limitations of Seele's sandbox, it always occupies about 16 MiB of memory before starting the judge program. If the value of memory_kib is too small, it may cause the sandbox to fail to be created.

Judge Report

The judge report returned by compilation tasks and execution tasks contains the following properties:

NameTypeDescription
statusstringThe exit status of the program
exit_codenumberThe exit code returned by the program
signalstringProvided only when the program is terminated by a signal, the name of the corresponding signal. See zerrors_linux_amd64.go (opens in a new tab)
wall_time_msnumberThe time elapsed from the program's launch to termination, in ms
cpu_user_time_msnumberThe total user-mode CPU time consumed by the program execution, in ms
cpu_kernel_time_msnumberThe total kernel-mode CPU time consumed by the program execution, in ms
memory_usage_kibnumberThe peak memory usage of the program during execution, in KiB

wall_time_ms is the time measured externally by the sandbox, and there may be some deviation compared to the actual elapsed time from the start to the end of the program execution. Tests show that this error is generally at the level of a few milliseconds. We recommend users to use cpu_user_time_ms to compare the pros and cons of different judge programs in terms of execution time.

⚠️

Due to the reasons mentioned above for the sandbox, the memory_usage_kib in the judge report is often greater than approximately 12 MiB. When the memory used by the judge program is less than the memory occupied by the sandbox before starting the judge program, memory_usage_kib cannot reflect the true memory size occupied by the judge program.

status attribute

Indicates the reason for the program termination. Its values are as shown in the table below:

ValueDescription
NORMALThe program ends normally, without any runtime crashes or resource limit violations
RUNTIME_ERRORThe program ends with a runtime crash, and the exit code is not 0
SIGNAL_TERMINATEThe program is terminated by a signal sent by the Linux kernel. For example, when dividing by 0, the program is terminated by the SIGFPE signal
USER_TIME_LIMIT_EXCEEDEDThe user-mode CPU time consumed by the program exceeds the limit
WALL_TIME_LIMIT_EXCEEDEDThe program is terminated due to prolonged execution time
MEMORY_LIMIT_EXCEEDEDThe program is terminated for attempting to allocate memory beyond the limit
OUTPUT_LIMIT_EXCEEDEDThe program outputs more data than the limit
UNKNOWNUnknown reason, the sandbox may have a bug