From 19bd742bc9434ea401964db30fa904c6c9c68bce Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Fri, 15 Apr 2022 21:29:55 +0200 Subject: [PATCH] Show a localized message if the program was killed. * This will most likely happen by the OOM killer, thus we inform the user about the memory restriction. --- app/assets/javascripts/editor/editor.js.erb | 7 +++++++ app/assets/javascripts/editor/evaluation.js | 5 +++++ app/assets/javascripts/editor/execution.js | 1 + app/assets/javascripts/shell.js | 17 ++++++++++++++--- app/controllers/submissions_controller.rb | 1 + app/models/runner.rb | 7 +++++-- .../execution_environments/shell.html.slim | 2 +- app/views/exercises/_editor.html.slim | 2 +- config/locales/de.yml | 1 + config/locales/en.yml | 1 + 10 files changed, 37 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb index 93e9702f..d40badea 100644 --- a/app/assets/javascripts/editor/editor.js.erb +++ b/app/assets/javascripts/editor/editor.js.erb @@ -743,6 +743,13 @@ var CodeOceanEditor = { }); }, + showOutOfMemoryMessage: function () { + $.flash.info({ + icon: ['fa', 'fa-clock-o'], + text: $('#editor').data('message-out-of-memory') + }); + }, + showTimeoutMessage: function () { $.flash.info({ icon: ['fa', 'fa-clock-o'], diff --git a/app/assets/javascripts/editor/evaluation.js b/app/assets/javascripts/editor/evaluation.js index 0e511d78..922ba63d 100644 --- a/app/assets/javascripts/editor/evaluation.js +++ b/app/assets/javascripts/editor/evaluation.js @@ -102,6 +102,11 @@ CodeOceanEditorEvaluation = { })) { this.showTimeoutMessage(); } + if (_.some(response, function (result) { + return result.status === 'out_of_memory'; + })) { + this.showOutOfMemoryMessage(); + } if (_.some(response, function (result) { return result.status === 'container_depleted'; })) { diff --git a/app/assets/javascripts/editor/execution.js b/app/assets/javascripts/editor/execution.js index 8ac6df4d..6b2df68b 100644 --- a/app/assets/javascripts/editor/execution.js +++ b/app/assets/javascripts/editor/execution.js @@ -47,6 +47,7 @@ CodeOceanEditorWebsocket = { this.websocket.on('render', this.renderWebsocketOutput.bind(this)); this.websocket.on('exit', this.handleExitCommand.bind(this)); this.websocket.on('timeout', this.showTimeoutMessage.bind(this)); + this.websocket.on('out_of_memory', this.showOutOfMemoryMessage.bind(this)); this.websocket.on('status', this.showStatus.bind(this)); this.websocket.on('hint', this.showHint.bind(this)); }, diff --git a/app/assets/javascripts/shell.js b/app/assets/javascripts/shell.js index 6b875e18..73ba02ae 100644 --- a/app/assets/javascripts/shell.js +++ b/app/assets/javascripts/shell.js @@ -29,10 +29,14 @@ $(document).on('turbolinks:load', function () { }; const handleResponse = function (response) { + // Always print stdout and stderr + printOutput(response); + + // If an error occurred, print it too if (response.status === 'timeout') { printTimeout(response); - } else { - printOutput(response); + } else if (response.status === 'out_of_memory') { + printOutOfMemory(response); } }; @@ -71,12 +75,19 @@ $(document).on('turbolinks:load', function () { }; const printTimeout = function (output) { - const element = $.append('

'); + const element = $('

'); element.addClass('text-danger'); element.text($('#shell').data('message-timeout')); $('#output').append(element); }; + const printOutOfMemory = function (output) { + const element = $('

'); + element.addClass('text-danger'); + element.text($('#shell').data('message-out-of-memory')); + $('#output').append(element); + }; + if ($('#shell').isPresent()) { const command = $('#command') command.focus(); diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 83fefdb4..fa095e36 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -157,6 +157,7 @@ class SubmissionsController < ApplicationController "\n#{t('exercises.implement.exit_failure', timestamp: l(Time.zone.now, format: :short), exit_code: exit_code)}" end client_socket.send_data JSON.dump({cmd: :write, stream: :stdout, data: "#{exit_statement}\n"}) + client_socket.send_data JSON.dump({cmd: :out_of_memory}) if exit_code == 137 close_client_connection(client_socket) end diff --git a/app/models/runner.rb b/app/models/runner.rb index 67edef2e..458ae144 100644 --- a/app/models/runner.rb +++ b/app/models/runner.rb @@ -78,13 +78,14 @@ class Runner < ApplicationRecord stdout = +'' stderr = +'' try = 0 + + exit_code = 1 # default to error begin if try.nonzero? request_new_id save end - exit_code = 1 # default to error execution_time = attach_to_execution(command) do |socket| socket.on :stderr do |data| stderr << data @@ -120,7 +121,9 @@ class Runner < ApplicationRecord # We forward the exception if requested raise e if raise_exception && defined?(e) && e.present? - output.merge!(stdout: stdout, stderr: stderr) + # If the process was killed with SIGKILL, it is most likely that the OOM killer was triggered. + output[:status] = :out_of_memory if exit_code == 137 + output.merge!(stdout: stdout, stderr: stderr, exit_code: exit_code) end end diff --git a/app/views/execution_environments/shell.html.slim b/app/views/execution_environments/shell.html.slim index ae35c7b8..95b4cf18 100644 --- a/app/views/execution_environments/shell.html.slim +++ b/app/views/execution_environments/shell.html.slim @@ -1,6 +1,6 @@ h1 = @execution_environment -#shell data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @execution_environment.permitted_execution_time) data-url=execute_command_execution_environment_path(@execution_environment) +#shell data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @execution_environment.permitted_execution_time) data-message-out-of-memory=t('exercises.editor.out_of_memory', memory_limit: @execution_environment.memory_limit) data-url=execute_command_execution_environment_path(@execution_environment) .form-group label for='command' = t('.command') input#command.form-control type='text' diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index aadcffa2..59132d26 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -5,7 +5,7 @@ - show_rfc_interventions = @show_rfc_interventions || "false" - show_tips_interventions = @show_tips_interventions || "false" - hide_rfc_button = @hide_rfc_button || false -#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-tips-interventions=show_tips_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) +#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-message-out-of-memory=t('exercises.editor.out_of_memory', memory_limit: @exercise.execution_environment.memory_limit) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-tips-interventions=show_tips_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) - unless @embed_options[:hide_sidebar] - additional_classes = 'sidebar-col' diff --git a/config/locales/de.yml b/config/locales/de.yml index 10dc62ab..8f5139de 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -360,6 +360,7 @@ de: submit_after_late_deadline: Code verspätet zur Bewertung abgeben test: Testen timeout: 'Ausführung gestoppt. Ihr Code hat die erlaubte Ausführungszeit von %{permitted_execution_time} Sekunden überschritten.' + out_of_memory: 'Ausführung gestoppt. Ihr Code hat den erlaubten Arbeitsspeicher von %{memory_limit} MB überschritten.' exercise_deadline_passed: 'Das Ergebnis kann nicht übertragen werden.' tooltips: save: Ihr Code wird automatisch gespeichert, wann immer Sie eine Datei herunterladen, ausführen oder testen. Explizites Speichern ist also selten notwendig. diff --git a/config/locales/en.yml b/config/locales/en.yml index e7123be7..2cf770cb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -360,6 +360,7 @@ en: submit_after_late_deadline: Submit Code for Assessment After Deadline Passed test: Test timeout: 'Execution stopped. Your code exceeded the permitted execution time of %{permitted_execution_time} seconds.' + out_of_memory: 'Execution stopped. Your code exceeded the permitted RAM usage of %{memory_limit} MB.' exercise_deadline_passed: 'The score cannot be submitted.' tooltips: save: Your code is automatically saved whenever you download, run, or test it. Therefore, explicitly saving is rarely necessary.