Skip to content
Extraits de code Groupes Projets
Valider 30d371e2 rédigé par Colin Evrard's avatar Colin Evrard
Parcourir les fichiers

Add tests data

parent 2eaaab87
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 1422 ajouts et 23 suppressions
LSINF1252/
venv/
\ No newline at end of file
Fichier ajouté
import unittest
from tests.test_task_data import TaskDataTestCase
def suite():
suite = unittest.TestSuite()
suite.addTest(TaskDataTestCase('test_task_dir_to_TaskData'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
\ No newline at end of file
...@@ -2,13 +2,13 @@ from typing import Optional, List, Callable, Any ...@@ -2,13 +2,13 @@ from typing import Optional, List, Callable, Any
from pathlib import Path from pathlib import Path
import yaml import yaml
import os import os
import subprocess, shlex, re, os, yaml
from inginious import feedback, rst, input
import logging import logging
import subprocess, shlex, re, os, yaml import subprocess, shlex, re, os, yaml
LOG_FMT = '%(asctime)s %(message)s' from dataclasses import dataclass
logging.basicConfig(format=FORMAT) from itertools import chain
logging.basicConfig()
logger = logging.getLogger('JudgeAPI') logger = logging.getLogger('JudgeAPI')
logger.setLevel('DEBUG')
""" """
Task common, the file defining the API that will be used for the C judge system. Task common, the file defining the API that will be used for the C judge system.
...@@ -56,29 +56,28 @@ def task_dir_validate(task_dir_path: Path) -> bool: ...@@ -56,29 +56,28 @@ def task_dir_validate(task_dir_path: Path) -> bool:
return False return False
#list task files #list task files
dir_content = os.listdir(task_dir_path) dir_content = os.listdir(task_dir_path)
dir_files = {str(f).lower():f for f in dir_content if os.path.isfile(f)} dir_files = {str(f).lower():f for f in dir_content if os.path.isfile(os.path.join(task_dir_path, f))}
dir_subdirs = {str(d).lower():d for d in dir_content if os.path.isdir(d)} dir_subdirs = {str(d).lower():d for d in dir_content if os.path.isdir(os.path.join(task_dir_path, d))}
#check for mandatory files and subdirectories #check for mandatory files and subdirectories
if "task.yaml" not in dir_files and "task.yml" not in dir_files: if "task.yaml" not in dir_files and "task.yml" not in dir_files:
logger.debug(f"Could not find task.yaml in {str(task_dir_path)}") logger.warning(f"Could not find task.yaml in {str(task_dir_path)}")
return False return False
if "run" not in dir_files: if "run" not in dir_files:
logger.debug(f"Could not find run file in {task_dir_path}") logger.warning(f"Could not find run file in {task_dir_path}")
return False return False
if "student" not in dir_subdirs: if "student" not in dir_subdirs:
logger.debug(f"Could not find student folder in {task_dir_path}. Maybe the tasks are too simple for using this API ?") logger.warning(f"Could not find student folder in {task_dir_path}. Maybe the tasks are too simple for using this API ?")
return False return False
#check the content of the student directory #check the content of the student directory
student_dir = os.path.join(task_dir_path, dir_subdirs['student']) student_dir = os.path.join(task_dir_path, dir_subdirs['student'])
student_dir_content_splitted = [str(c).to_lower().split('.') for c in os.listdir(student_dir)] student_dir_content_splitted = [str(c).lower().split('.') for c in os.listdir(student_dir)]
student_dir_content_ext = [splitted[1] for c in student_dir_content_splitted if len(splitted) > 1] student_dir_content_ext = [splitted[1:] for splitted in student_dir_content_splitted if len(splitted) > 1]
if 'tpl' not in student_dir_content_ext: if 'tpl' not in chain(*student_dir_content_ext):
logger.debug(f"Could not find template file in {str(student_dir)}") logger.warning(f"Could not find template file in {str(student_dir)}")
return False return False
logger.debug(f"Validated task directory {str(task_dir_path)}") logger.debug(f"Validated task directory {str(task_dir_path)}")
...@@ -123,18 +122,20 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs: ...@@ -123,18 +122,20 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs:
#discover files and folders #discover files and folders
task_dir_content = {str(content).lower():content for content in os.listdir(task_dir_path)} task_dir_content = {str(content).lower():content for content in os.listdir(task_dir_path)}
task_dir_files = {name:path for name, path in task_dir_content.items() if os.path.isfile(path)} task_dir_files = {name:path for name, path in task_dir_content.items() if os.path.isfile(os.path.join(task_dir_path, path))}
task_dir_subdirs = {name:path for name, path in task_dir_content.items() if os.path.isdir(s)(path)} task_dir_subdirs = {name:path for name, path in task_dir_content.items() if os.path.isdir(os.path.join(task_dir_path, path))}
#Guess the yaml extension to the task file and load it into a dict #Guess the yaml extension to the task file and load it into a dict
task_file = task_dir_files['task.yaml'] if 'task.yaml' in task_dir_files else task_dir_files['task.yml'] task_file = task_dir_files['task.yaml'] if 'task.yaml' in task_dir_files else task_dir_files['task.yml']
with open(task_file, 'r') as f: with open(os.path.join(task_dir_path, task_file), 'r') as f:
TaskData_init_kwargs['task'] = yaml.load(f) TaskData_init_kwargs['task'] = yaml.safe_load(f)
student_dir_path = os.path.join(task_dir_path, task_dir_content['student'])
#discover files and folders of the student directory #discover files and folders of the student directory
student_dir_content = {str(content).lower():content for content in os.listdir(os.path.join(task_dir_path, task_dir_content['student']))} student_dir_content = {str(content).lower():content for content in os.listdir(student_dir_path)}
student_dir_files = {name:path for name, path in student_dir_content.items() if os.path.isfile(path)} student_dir_files = {name:path for name, path in student_dir_content.items() if os.path.isfile(os.path.join(student_dir_path, path))}
student_dir_subdirs = {name:path for name, path in student_dir_content.items() if os.path.isdir(s)(path)} student_dir_subdirs = {name:path for name, path in student_dir_content.items() if os.path.isfile(os.path.join(student_dir_path, path))}
#retrieve the template file and the annex files #retrieve the template file and the annex files
annex = [] annex = []
...@@ -155,12 +156,12 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs: ...@@ -155,12 +156,12 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs:
return TaskData(**TaskData_init_kwargs) return TaskData(**TaskData_init_kwargs)
def student_code_generate(task: TaskData): def student_code_generate(task: TaskData, generator: Callable[[str, str], None]):
filename = task.template.name filename = task.template.name
filename_components = filename.split(".") filename_components = filename.split(".")
extension = filename_components[1] if len(filename_components) > 2 else "" extension = filename_components[1] if len(filename_components) > 2 else ""
output_name = f"{filename_components[0]}{extension}" output_name = f"{filename_components[0]}{extension}"
input.parse_template(filename, output_name) generator(filename, output_name)
task.student_code = output_name task.student_code = output_name
logger.debug(f"Generated student code: {output_name}") logger.debug(f"Generated student code: {output_name}")
......
Fichier ajouté
Fichier ajouté
#!/bin/python3
# Script d'interface entre INGInious et des tests unitaires écrits à l'aide de CUnit
# Auteurs : Mathieu Xhonneux, Anthony Gégo
# Licence : GPLv3
import subprocess, shlex, re, os, yaml
from inginious import feedback, rst, input
# Switch working directory to student/
os.chdir("student")
# Fetch and save the student code into a file for compilation
input.parse_template("student_code.c.tpl", "student_code.c")
# Compilation
p = subprocess.Popen(shlex.split("make"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
make_output = p.communicate()[0].decode('utf-8')
# If compilation failed, exit with "failed" result
if p.returncode:
feedback.set_tag("not_compile", True)
feedback.set_global_result("failed")
feedback.set_global_feedback("La compilation de votre code a échoué. Voici le message de sortie de la commande ``make`` :")
feedback.set_global_feedback(rst.get_codeblock('', make_output), True)
exit(0)
else:
# Cppcheck
p = subprocess.Popen(shlex.split("make check"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
cppcheck_output = p.communicate()[0].decode('utf-8')
if p.returncode:
feedback.set_tag("cppcheck", True)
feedback.set_global_result("failed")
feedback.set_global_feedback("La compilation de votre code avec ``cppcheck`` a échoué. Voici le message de sortie de la commande ``make check`` :")
feedback.set_global_feedback(rst.get_codeblock('', cppcheck_output), True)
exit(0)
else:
feedback.set_global_result("success")
feedback.set_global_feedback("- Votre code compile.\n")
# Parse banned functions
try:
banned_funcs = re.findall("BAN_FUNCS\(([a-zA-Z0-9_, ]*)\)", open('tests.c').read())[-1].replace(" ", "").split(",")
banned_funcs = list(filter(None, banned_funcs))
except IndexError:
banned_funcs = []
if banned_funcs:
p = subprocess.Popen(shlex.split("readelf -s student_code.o"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
readelf_output = p.communicate()[0].decode('utf-8')
for func in banned_funcs:
if re.search("UND {}\n".format(func), readelf_output):
feedback.set_tag("banned_funcs", True)
feedback.set_global_result("failed")
feedback.set_global_feedback("Vous utilisez la fonction {}, qui n'est pas autorisée.".format(func))
exit(0)
# Remove source files
subprocess.run("rm -rf *.c *.tpl *.h *.o", shell=True)
LANG = input.get_input('@lang')
# Run the code in a parallel container
p = subprocess.Popen(shlex.split("run_student --time 20 --hard-time 60 ./tests LANGUAGE={}".format(LANG)), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
o, e = p.communicate()
print(o.decode("utf-8"))
# If run failed, exit with "failed" result
if p.returncode:
feedback.set_global_result("failed")
if p.returncode == 256-8:
montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur. Le signal SIGFPE a été envoyé : *Floating Point Exception*.")
feedback.set_tag("sigfpe", True)
elif p.returncode == 256-11:
montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur. Le signal SIGSEGV a été envoyé : *Segmentation Fault*.")
elif p.returncode == 252:
montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a tenté d'allouer plus de mémoire que disponible.")
feedback.set_tag("memory", True)
elif p.returncode == 253:
montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a pris trop de temps pour s'exécuter.")
else:
montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur.")
feedback.set_global_feedback(rst.indent_block(2, montest_output, " "), True)
exit(0)
#elif run_output:
# feedback.set_global_feedback("- Sortie de votre méthode de test:\n" + rst.indent_block(2, rst.get_codeblock('', run_output), " "), True)
# Comment to run the tests
#feedback.set_global_feedback("- **Cette note n'est pas finale.** Une série de tests sera exécutée sur votre code après l'examen.\n", True)
#exit(0)
# Fetch CUnit test results
results_raw = [r.split('#') for r in open('results.txt').read().splitlines()]
results = [{'pid':r[0], 'code':r[1], 'desc':r[2], 'weight':int(r[3]), 'tags': r[4].split(","), 'info_msgs':r[5:]} for r in results_raw]
# Produce feedback
if all([r['code'] == 'SUCCESS' for r in results]):
feedback.set_global_feedback("\n- Votre code a passé tous les tests.", True)
else:
feedback.set_global_feedback("\n- Il y a des erreurs dans votre solution.", True)
score = 0
total = 0
tests_result = {}
for test in results:
total += test['weight']
for tag in test['tags']:
if tag != "":
feedback.set_tag(tag, True)
if test['code'] == 'SUCCESS':
score += test['weight']
feedback.set_problem_feedback("* {desc}\n\n => réussi ({weight}/{weight}) pts)\n\n".format(**test)+(" Info: {}\n\n".format("".join(test['info_msgs'])) if test['info_msgs'] else '\n'),
test['pid'], True)
tests_result[test['pid']] = True if tests_result.get(test['pid'], True) else False
else:
feedback.set_problem_feedback("* {desc}\n\n => échoué (0/{weight}) pts)\n\n".format(**test)+(" Info: {}\n\n".format("".join(test['info_msgs'])) if test['info_msgs'] else '\n'),
test['pid'], True)
tests_result[test['pid']] = False
for pid, result in tests_result.items():
if result:
feedback.set_problem_result("success", pid)
else:
feedback.set_problem_result("failed", pid)
with open("../task.yaml", 'r') as stream:
problems = yaml.load(stream)['problems']
for name, meta in problems.items():
if meta['type'] == 'match':
answer = input.get_input(name)
if answer == meta['answer']:
feedback.set_problem_result("success", name)
feedback.set_problem_feedback("Votre réponse est correcte. (1/1 pts)", name, True)
score += 1
else:
feedback.set_problem_result("failed", name)
feedback.set_problem_feedback("Votre réponse est incorrecte. (0/1 pts)", name, True)
total += 1
score = 100*score/(total if not total == 0 else 1)
feedback.set_grade(score)
feedback.set_global_result("success" if score >= 50 else "failed")
#include <stdlib.h>
char *buf_strcpy(const char *src){
int len = strlen(src) + 1, i;
char * ret = (char*) malloc(sizeof(char)*len);
if (!ret)
return NULL;
for(i = 0; i < len; i++)
*(ret+i) = *(src+i);
return ret;
}
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include <CUnit/Automated.h>
#include <libintl.h>
#include <locale.h>
#define _(STRING) gettext(STRING)
#include <dlfcn.h>
#include <malloc.h>
#include "wrap.h"
#define TAGS_NB_MAX 20
#define TAGS_LEN_MAX 30
extern bool wrap_monitoring;
extern struct wrap_stats_t stats;
extern struct wrap_monitor_t monitored;
extern struct wrap_fail_t failures;
extern struct wrap_log_t logs;
extern sigjmp_buf segv_jmp;
int true_stderr;
int true_stdout;
int pipe_stderr[2], usr_pipe_stderr[2];
int pipe_stdout[2], usr_pipe_stdout[2];
extern int stdout_cpy, stderr_cpy;
struct itimerval it_val;
CU_pSuite pSuite = NULL;
struct info_msg {
char *msg;
struct info_msg *next;
};
struct __test_metadata {
struct info_msg *fifo_in;
struct info_msg *fifo_out;
char problem[140];
char descr[250];
unsigned int weight;
unsigned char nb_tags;
char tags[TAGS_NB_MAX][TAGS_LEN_MAX];
int err;
} test_metadata;
void set_test_metadata(char *problem, char *descr, unsigned int weight)
{
test_metadata.weight = weight;
strncpy(test_metadata.problem, problem, sizeof(test_metadata.problem));
strncpy(test_metadata.descr, descr, sizeof(test_metadata.descr));
}
void push_info_msg(char *msg)
{
if (strstr(msg, "#") != NULL || strstr(msg, "\n") != NULL) {
test_metadata.err = EINVAL;
return;
}
struct info_msg *item = malloc(sizeof(struct info_msg));
if (item == NULL)
test_metadata.err = ENOMEM;
item->next = NULL;
item->msg = malloc(strlen(msg) + 1);
if (item->msg == NULL)
test_metadata.err = ENOMEM;
strcpy(item->msg, msg);
if (test_metadata.fifo_in == NULL && test_metadata.fifo_out == NULL) {
test_metadata.fifo_in = item;
test_metadata.fifo_out = item;
} else {
test_metadata.fifo_out->next = item;
test_metadata.fifo_out = item;
}
}
void set_tag(char *tag)
{
int i=0;
while (tag[i] != '\0' && i < TAGS_LEN_MAX) {
if (!isalnum(tag[i]) && tag[i] != '-' && tag[i] != '_')
return;
i++;
}
if (test_metadata.nb_tags < TAGS_NB_MAX)
strncpy(test_metadata.tags[test_metadata.nb_tags++], tag, TAGS_LEN_MAX);
}
void segv_handler(int sig, siginfo_t *unused, void *unused2) {
wrap_monitoring = false;
push_info_msg(_("Your code produced a segfault."));
set_tag("sigsegv");
wrap_monitoring = true;
siglongjmp(segv_jmp, 1);
}
void alarm_handler(int sig, siginfo_t *unused, void *unused2)
{
wrap_monitoring = false;
push_info_msg(_("Your code exceeded the maximal allowed execution time."));
set_tag("timeout");
wrap_monitoring = true;
siglongjmp(segv_jmp, 1);
}
int sandbox_begin()
{
// Start timer
it_val.it_value.tv_sec = 2;
it_val.it_value.tv_usec = 0;
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &it_val, NULL);
// Intercepting stdout and stderr
dup2(pipe_stdout[1], STDOUT_FILENO);
dup2(pipe_stderr[1], STDERR_FILENO);
// Emptying the user pipes
char buf[BUFSIZ];
int n;
while ((n = read(usr_pipe_stdout[0], buf, BUFSIZ)) > 0);
while ((n = read(usr_pipe_stderr[0], buf, BUFSIZ)) > 0);
wrap_monitoring = true;
return 0;
}
void sandbox_fail()
{
CU_FAIL("Segfault or timeout");
}
void sandbox_end()
{
wrap_monitoring = false;
// Remapping stderr to the orignal one ...
dup2(true_stdout, STDOUT_FILENO); // TODO
dup2(true_stderr, STDERR_FILENO);
// ... and looking for a double free warning
char buf[BUFSIZ];
int n;
while ((n = read(pipe_stdout[0], buf, BUFSIZ)) > 0) {
write(usr_pipe_stdout[1], buf, n);
write(STDOUT_FILENO, buf, n);
}
while ((n = read(pipe_stderr[0], buf, BUFSIZ)) > 0) {
if (strstr(buf, "double free or corruption") != NULL) {
CU_FAIL("Double free or corruption");
push_info_msg(_("Your code produced a double free."));
set_tag("double_free");
}
write(usr_pipe_stderr[1], buf, n);
write(STDERR_FILENO, buf, n);
}
it_val.it_value.tv_sec = 0;
it_val.it_value.tv_usec = 0;
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &it_val, NULL);
}
int init_suite1(void)
{
return 0;
}
int clean_suite1(void)
{
return 0;
}
void start_test()
{
bzero(&test_metadata,sizeof(test_metadata));
bzero(&stats,sizeof(stats));
bzero(&failures,sizeof(failures));
bzero(&monitored,sizeof(monitored));
bzero(&logs,sizeof(logs));
}
int __real_exit(int status);
int __wrap_exit(int status){
return status;
}
int run_tests(int argc, char *argv[], void *tests[], int nb_tests) {
for (int i=1; i < argc; i++) {
if (!strncmp(argv[i], "LANGUAGE=", 9))
putenv(argv[i]);
}
setlocale (LC_ALL, "");
bindtextdomain("tests", getenv("PWD"));
bind_textdomain_codeset("messages", "UTF-8");
textdomain("tests");
mallopt(M_PERTURB, 142); // newly allocated memory with malloc will be set to ~142
// Code for detecting properly double free errors
mallopt(M_CHECK_ACTION, 1); // don't abort if double free
true_stderr = dup(STDERR_FILENO); // preparing a non-blocking pipe for stderr
true_stdout = dup(STDOUT_FILENO); // preparing a non-blocking pipe for stderr
int *pipes[] = {pipe_stderr, pipe_stdout, usr_pipe_stdout, usr_pipe_stderr};
for(int i=0; i < 4; i++) { // Configuring pipes to be non-blocking
pipe(pipes[i]);
int flags = fcntl(pipes[i][0], F_GETFL, 0);
fcntl(pipes[i][0], F_SETFL, flags | O_NONBLOCK);
}
stdout_cpy = usr_pipe_stdout[0];
stderr_cpy = usr_pipe_stderr[0];
putenv("LIBC_FATAL_STDERR_=2"); // needed otherwise libc doesn't print to program's stderr
/* make sure that we catch segmentation faults */
struct sigaction sa;
memset(&sa, 0, sizeof(sigaction));
sigemptyset(&sa.sa_mask);
static char stack[SIGSTKSZ];
stack_t ss = {
.ss_size = SIGSTKSZ,
.ss_sp = stack,
};
sa.sa_flags = SA_NODEFER|SA_ONSTACK|SA_RESTART;
sa.sa_sigaction = segv_handler;
sigaltstack(&ss, 0);
sigfillset(&sa.sa_mask);
int ret = sigaction(SIGSEGV, &sa, NULL);
if (ret)
return ret;
sa.sa_sigaction = alarm_handler;
ret = sigaction(SIGALRM, &sa, NULL);
if (ret)
return ret;
/* Output file containing succeeded / failed tests */
FILE* f_out = fopen("results.txt", "w");
if (!f_out)
return -ENOENT;
/* initialize the CUnit test registry */
if (CUE_SUCCESS != CU_initialize_registry())
return CU_get_error();
/* add a suite to the registry */
pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1);
if (NULL == pSuite) {
CU_cleanup_registry();
return CU_get_error();
}
for (int i=0; i < nb_tests; i++) {
Dl_info DlInfo;
if (dladdr(tests[i], &DlInfo) == 0)
return -EFAULT;
CU_pTest pTest;
if ((pTest = CU_add_test(pSuite, DlInfo.dli_sname, tests[i])) == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
printf("\n==== Results for test %s : ====\n", DlInfo.dli_sname);
start_test();
if (CU_basic_run_test(pSuite,pTest) != CUE_SUCCESS)
return CU_get_error();
if (test_metadata.err)
return test_metadata.err;
int nb = CU_get_number_of_tests_failed();
if (nb > 0)
ret = fprintf(f_out, "%s#FAIL#%s#%d#", test_metadata.problem,
test_metadata.descr, test_metadata.weight);
else
ret = fprintf(f_out, "%s#SUCCESS#%s#%d#", test_metadata.problem,
test_metadata.descr, test_metadata.weight);
if (ret < 0)
return ret;
for(int i=0; i < test_metadata.nb_tags; i++) {
ret = fprintf(f_out, "%s", test_metadata.tags[i]);
if (ret < 0)
return ret;
if (i != test_metadata.nb_tags - 1) {
ret = fprintf(f_out, ",");
if (ret < 0)
return ret;
}
}
while (test_metadata.fifo_in != NULL) {
struct info_msg *head = test_metadata.fifo_in;
ret = fprintf(f_out, "#%s", head->msg);
if (head->msg != NULL)
free(head->msg);
test_metadata.fifo_in = head->next;
free(head);
if (ret < 0)
return ret;
}
test_metadata.fifo_out = NULL;
ret = fprintf(f_out, "\n");
if (ret < 0)
return ret;
}
fclose(f_out);
/* Run all tests using the CUnit Basic interface */
//CU_basic_run_tests();
//CU_automated_run_tests();
CU_cleanup_registry();
return CU_get_error();
}
#include <sys/mman.h>
#include <CUnit/CUnit.h>
#include <setjmp.h>
#include "wrap.h"
#include "trap.h"
#include <libintl.h>
#include <locale.h>
#define _(STRING) gettext(STRING)
#define RUN(...) void *ptr_tests[] = {__VA_ARGS__}; return run_tests(argc, argv, ptr_tests, sizeof(ptr_tests)/sizeof(void*))
#define BAN_FUNCS(...)
#define SANDBOX_BEGIN sandbox_begin(); if(sigsetjmp(segv_jmp,1) == 0) { (void)0
#define SANDBOX_END } else { \
sandbox_fail(); \
} \
sandbox_end()
// Hidden by macros
int run_tests(int argc, char *argv[], void *tests[], int nb_tests);
int sandbox_begin();
void sandbox_fail();
void sandbox_end();
// To use inside tests
void set_test_metadata(char *problem, char *descr, unsigned int weight);
void push_info_msg(char *msg);
void set_tag(char *tag);
// Set to true to enable monitoring features
bool wrap_monitoring = false;
struct wrap_stats_t stats;
struct wrap_monitor_t monitored;
struct wrap_fail_t failures;
struct wrap_log_t logs;
int stdout_cpy, stderr_cpy;
sigjmp_buf segv_jmp;
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include "trap.h"
void *trap_buffer(size_t size, int type, int flags, void *data)
{
int nb_pages = (size / getpagesize()) + 2;
void *ptr = mmap(NULL, getpagesize() * nb_pages, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) {
return NULL;
}
void *buf_start;
if (type == TRAP_LEFT) {
buf_start = ptr + getpagesize();
mprotect(ptr, getpagesize(), PROT_NONE);
} else if (type == TRAP_RIGHT) {
buf_start = ptr + (nb_pages - 1) * getpagesize() - size;
mprotect(ptr + (nb_pages - 1)*getpagesize(), getpagesize(), PROT_NONE);
} else {
return NULL;
}
if (data != NULL)
memcpy(buf_start, data, size);
mprotect(buf_start, size, flags);
return buf_start;
}
int free_trap(void *ptr, size_t size)
{
return munmap(ptr, size);
}
enum {
TRAP_LEFT,
TRAP_RIGHT
};
void *trap_buffer(size_t size, int type, int flags, void *data);
int free_trap(void *ptr, size_t size);
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
// headers for all stats
#include "wrap_getpid.h"
#include "wrap_file.h"
#include "wrap_malloc.h"
#include "wrap_mutex.h"
#include "wrap_sleep.h"
// Basic structures for system call wrapper
// verifies whether the system call needs to be monitored. Each
// supported system call must be referenced here
// flag must be set to true if the system call must be monitored
struct wrap_monitor_t {
bool getpid;
bool open;
bool creat;
bool close;
bool read;
bool write;
bool stat;
bool fstat;
bool lseek;
bool free;
bool malloc;
bool calloc;
bool realloc;
bool pthread_mutex_lock;
bool pthread_mutex_trylock;
bool pthread_mutex_unlock;
bool pthread_mutex_init;
bool pthread_mutex_destroy;
bool sleep;
};
#define MAX_LOG 1000
// log for specific system calls
struct malloc_t {
int n;
struct malloc_elem_t log[MAX_LOG];
};
struct wrap_log_t {
struct malloc_t malloc;
} ;
// failures are defined as a bitmask. If set to zero, the system call
// never fails. Otherwise, the fail information is shifted to the right
// and the next system call will fail if its low order bit is set
// we assume that there are no more than 32 system calls that need
// fail. This should be sufficient for most inginious exercices.
// If not, the idea can be extended to support more bits.
#define FAIL_ALWAYS 0b11111111111111111111111111111111
#define FAIL_FIRST 0b00000000000000000000000000000001
#define FAIL_TWICE 0b00000000000000000000000000000011
#define FAIL_NEVER 0b00000000000000000000000000000000
#define FAIL_SECOND 0b00000000000000000000000000000010
#define FAIL_THIRD 0b00000000000000000000000000000100
#define FAIL(v) (((v & 0b00000000000000000000000000000001) == 0b00000000000000000000000000000001) )
#define NEXT(v) (v==FAIL_ALWAYS ? FAIL_ALWAYS : v >> 1)
struct wrap_fail_t {
// bool getpid - this call cannot fail
uint32_t open; // indicates whether next open will fail
int open_ret; // return value if open fails
int open_errno; // errno value set if open fails
uint32_t creat;
int creat_ret;
int creat_errno;
uint32_t close;
int close_ret;
int close_errno;
uint32_t read;
int read_ret;
int read_errno;
uint32_t write;
int write_ret;
int write_errno;
uint32_t stat;
int stat_ret;
int stat_errno;
uint32_t fstat;
int fstat_ret;
int fstat_errno;
uint32_t lseek;
int lseek_ret;
int lseek_errno;
uint32_t malloc;
void *malloc_ret;
uint32_t calloc;
void *calloc_ret;
uint32_t realloc;
void *realloc_ret;
uint32_t free;
uint32_t pthread_mutex_lock;
int pthread_mutex_lock_ret;
int pthread_mutex_lock_errno;
uint32_t pthread_mutex_trylock;
int pthread_mutex_trylock_ret;
int pthread_mutex_trylock_errno;
uint32_t pthread_mutex_unlock;
int pthread_mutex_unlock_ret;
int pthread_mutex_unlock_errno;
uint32_t pthread_mutex_init;
int pthread_mutex_init_ret;
int pthread_mutex_init_errno;
uint32_t pthread_mutex_destroy;
int pthread_mutex_destroy_ret;
int pthread_mutex_destroy_errno;
uint32_t sleep;
unsigned int sleep_ret;
} ;
struct wrap_stats_t {
struct stats_getpid_t getpid;
struct stats_open_t open;
struct stats_creat_t creat;
struct stats_close_t close;
struct stats_read_t read;
struct stats_write_t write;
struct stats_stat_t stat;
struct stats_fstat_t fstat;
struct stats_lseek_t lseek;
struct stats_malloc_t malloc;
struct stats_calloc_t calloc;
struct stats_memory_t memory;
struct stats_free_t free;
struct stats_realloc_t realloc;
struct stats_pthread_mutex_lock_t pthread_mutex_lock;
struct stats_pthread_mutex_trylock_t pthread_mutex_trylock;
struct stats_pthread_mutex_unlock_t pthread_mutex_unlock;
struct stats_pthread_mutex_unlock_t pthread_mutex_init;
struct stats_pthread_mutex_unlock_t pthread_mutex_destroy;
struct stats_sleep_t sleep;
};
// wrapper for the file operations, open, read, write
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "wrap.h"
int __real_open(const char *pathname, int flags, mode_t mode);
int __real_creat(const char *pathname, mode_t mode);
int __real_close(int fd);
ssize_t __real_read(int fd, void *buf, size_t count);
ssize_t __real_write(int fd, const void *buf, size_t count);
int __real_stat(const char* path, struct stat *buf);
int __real_fstat(int fd, struct stat *buf);
int __real_lstat(const char* path, struct stat *buf);
ssize_t __real_pread(int fd, void *buf, size_t count, off_t offset);
ssize_t __real_pwrite(int fd, const void *buf, size_t count, off_t offset);
off_t __real_lseek(int fd, off_t offset, int whence);
extern bool wrap_monitoring;
extern struct wrap_stats_t stats;
extern struct wrap_monitor_t monitored;
extern struct wrap_fail_t failures;
extern struct wrap_log_t logs;
int __wrap_open(char *pathname, int flags, mode_t mode) {
if(!wrap_monitoring || !monitored.open) {
return __real_open(pathname,flags,mode);
}
stats.open.called++;
stats.open.last_params.pathname=pathname;
stats.open.last_params.flags=flags;
stats.open.last_params.mode=mode;
if (FAIL(failures.open)) {
failures.open=NEXT(failures.open);
errno=failures.open_errno;
stats.open.last_return=failures.open_ret;
return failures.open_ret;
}
failures.open=NEXT(failures.open);
// did not fail
int ret=__real_open(pathname, flags, mode);
stats.open.last_return=ret;
return ret;
}
int __wrap_creat(char *pathname, mode_t mode) {
if(!wrap_monitoring || !monitored.creat) {
return __real_creat(pathname,mode);
}
stats.creat.called++;
stats.creat.last_params.pathname=pathname;
stats.creat.last_params.mode=mode;
if (FAIL(failures.creat)) {
failures.creat=NEXT(failures.creat);
errno=failures.creat_errno;
stats.creat.last_return=failures.creat_ret;
return failures.creat_ret;
}
failures.creat=NEXT(failures.creat);
// did not fail
int ret=__real_creat(pathname, mode);
stats.creat.last_return=ret;
return ret;
}
int __wrap_close(int fd){
if(!wrap_monitoring || !monitored.close) {
return __real_close(fd);
}
stats.close.called++;
stats.close.last_params.fd=fd;
if (FAIL(failures.close)) {
failures.close=NEXT(failures.close);
errno=failures.close_errno;
stats.close.last_return=failures.close_ret;
return failures.close_ret;
}
failures.close=NEXT(failures.close);
// did not fail
int ret=__real_close(fd);
stats.close.last_return=ret;
return ret;
}
int __wrap_read(int fd, void *buf, size_t count){
if(!wrap_monitoring || !monitored.read) {
return __real_read(fd,buf,count);
}
stats.read.called++;
stats.read.last_params.fd=fd;
stats.read.last_params.buf=buf;
stats.read.last_params.count=count;
if (FAIL(failures.read)) {
failures.read=NEXT(failures.read);
errno=failures.read_errno;
stats.read.last_return=failures.read_ret;
return failures.read_ret;
}
failures.read=NEXT(failures.read);
// did not fail
int ret=__real_read(fd,buf,count);
stats.read.last_return=ret;
return ret;
}
int __wrap_write(int fd, void *buf, size_t count){
if(!wrap_monitoring || !monitored.write) {
return __real_write(fd,buf,count);
}
stats.write.called++;
stats.write.last_params.fd=fd;
stats.write.last_params.buf=buf;
stats.write.last_params.count=count;
if (FAIL(failures.write)) {
failures.write=NEXT(failures.write);
errno=failures.write_errno;
stats.write.last_return=failures.write_ret;
return failures.write_ret;
}
failures.write=NEXT(failures.write);
// did not fail
int ret=__real_write(fd,buf,count);
stats.write.last_return=ret;
return ret;
}
int __wrap_stat(char *path, struct stat *buf) {
if(!wrap_monitoring || !monitored.stat) {
return __real_stat(path,buf);
}
stats.stat.called++;
stats.stat.last_params.path=path;
stats.stat.last_params.buf=buf;
if (FAIL(failures.stat)) {
failures.stat=NEXT(failures.stat);
errno=failures.stat_errno;
stats.stat.last_return=failures.stat_ret;
return failures.stat_ret;
}
failures.stat=NEXT(failures.stat);
// did not fail
int ret=__real_stat(path,buf);
stats.stat.returned_stat.st_dev=buf->st_dev;
stats.stat.returned_stat.st_ino=buf->st_ino;
stats.stat.returned_stat.st_mode=buf->st_mode;
stats.stat.returned_stat.st_nlink=buf->st_nlink;
stats.stat.returned_stat.st_uid=buf->st_uid;
stats.stat.returned_stat.st_gid=buf->st_gid;
stats.stat.returned_stat.st_rdev=buf->st_rdev;
stats.stat.returned_stat.st_size=buf->st_size;
stats.stat.returned_stat.st_blksize=buf->st_blksize;
stats.stat.returned_stat.st_blocks=buf->st_blocks;
stats.stat.returned_stat.st_atime=buf->st_atime;
stats.stat.returned_stat.st_mtime=buf->st_mtime;
stats.stat.returned_stat.st_ctime=buf->st_ctime;
stats.stat.last_return=ret;
return ret;
}
int __wrap_fstat(int fd, struct stat *buf) {
if(!wrap_monitoring || !monitored.fstat) {
return __real_fstat(fd,buf);
}
stats.fstat.called++;
stats.fstat.last_params.fd=fd;
stats.fstat.last_params.buf=buf;
if (FAIL(failures.fstat)) {
failures.fstat=NEXT(failures.fstat);
errno=failures.fstat_errno;
return failures.fstat_ret;
}
failures.fstat=NEXT(failures.fstat);
// did not fail
int ret=__real_fstat(fd,buf);
stats.fstat.returned_stat.st_dev=buf->st_dev;
stats.fstat.returned_stat.st_ino=buf->st_ino;
stats.fstat.returned_stat.st_mode=buf->st_mode;
stats.fstat.returned_stat.st_nlink=buf->st_nlink;
stats.fstat.returned_stat.st_uid=buf->st_uid;
stats.fstat.returned_stat.st_gid=buf->st_gid;
stats.fstat.returned_stat.st_rdev=buf->st_rdev;
stats.fstat.returned_stat.st_size=buf->st_size;
stats.fstat.returned_stat.st_blksize=buf->st_blksize;
stats.fstat.returned_stat.st_blocks=buf->st_blocks;
stats.fstat.returned_stat.st_atime=buf->st_atime;
stats.fstat.returned_stat.st_mtime=buf->st_mtime;
stats.fstat.returned_stat.st_ctime=buf->st_ctime;
stats.fstat.last_return=ret;
return ret;
}
off_t __wrap_lseek(int fd, off_t offset, int whence) {
if(!wrap_monitoring || !monitored.lseek) {
return __real_lseek(fd,offset,whence);
}
stats.lseek.called++;
stats.lseek.last_params.fd=fd;
stats.lseek.last_params.offset=offset;
stats.lseek.last_params.whence=whence;
if (FAIL(failures.lseek)) {
failures.lseek=NEXT(failures.lseek);
errno=failures.lseek_errno;
return failures.lseek_ret;
}
failures.lseek=NEXT(failures.lseek);
// did not fail
off_t ret=__real_lseek(fd,offset,whence);
stats.lseek.last_return=ret;
return ret;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
// basic structure to record the parameters of the last open call
struct params_open_t {
char *pathname;
int flags;
mode_t mode;
};
// basic statistics for the utilisation of the open system call
struct stats_open_t {
int called; // number of times the open system call has been issued
struct params_open_t last_params; // parameters for the last call issued
int last_return; // return value of the last open call issued
};
struct params_creat_t {
char *pathname;
mode_t mode;
};
// basic statistics for the utilisation of the creat system call
struct stats_creat_t {
int called; // number of times the open system call has been issued
struct params_creat_t last_params; // parameters for the last call issued
int last_return; // return value of the last open call issued
};
struct params_close_t {
int fd;
};
// basic statistics for the utilisation of the close system call
struct stats_close_t {
int called; // number of times the open system call has been issued
struct params_close_t last_params; // parameters for the last call issued
int last_return; // return value of the last open call issued
};
struct params_read_t {
int fd;
void *buf;
ssize_t count;
};
// basic statistics for the utilisation of the read system call
struct stats_read_t {
int called; // number of times the read system call has been issued
struct params_read_t last_params; // parameters for the last call issued
int last_return; // return value of the last read call issued
};
struct params_write_t {
int fd;
void *buf;
ssize_t count;
};
// basic statistics for the utilisation of the write system call
struct stats_write_t {
int called; // number of times the write system call has been issued
struct params_read_t last_params; // parameters for the last call issued
int last_return; // return value of the last read call issued
};
struct params_stat_t {
char *path;
struct stat *buf;
};
// basic statistics for the utilisation of the stat system call
struct stats_stat_t {
int called; // number of times the write system call has been issued
struct params_stat_t last_params; // parameters for the last call issued
int last_return; // return value of the last read call issued
struct stat returned_stat; // last returned stat structure
};
struct params_fstat_t {
int fd;
struct stat *buf;
};
// basic statistics for the utilisation of the fstat system call
struct stats_fstat_t {
int called; // number of times the write system call has been issued
struct params_fstat_t last_params; // parameters for the last call issued
int last_return; // return value of the last read call issued
struct stat returned_stat; // last returned stat structure
};
struct params_lseek_t {
int fd;
off_t offset;
int whence;
};
// basic statistics for the utilisation of the fstat system call
struct stats_lseek_t {
int called; // number of times the lseek system call has been issued
struct params_lseek_t last_params; // parameters for the last call issued
int last_return; // return value of the last lseek call issued
};
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "wrap.h" // system call wrapper
// pid_t getpid(void);
pid_t __real_getpid(void);
extern bool wrap_monitoring;
extern struct wrap_stats_t stats;
extern struct wrap_monitor_t monitored;
extern struct wrap_fail_t failures;
void init_getpid() {
// nothing to do
}
void clean_getpid() {
// nothing to do
}
void resetstats_getpid() {
stats.getpid.called=0;
stats.getpid.last_return=0;
}
pid_t __wrap_getpid(void) {
if(!wrap_monitoring || !monitored.getpid) {
return __real_getpid();
}
// being monitored
stats.getpid.called++;
pid_t ret=__real_getpid();
stats.getpid.last_return=ret;
return ret;
}
// never remove statistics from this structure, they could be
// used by existing exercices. You might add some additional information
// if it can help to validate some exercices
#include <sys/types.h>
#include <unistd.h>
struct stats_getpid_t {
int called; // number of times the system call has been called
pid_t last_return; // last return value for getpid
};
void init_getpid();
void clean_getpid();
void resetstats_getpid();
/*
* Wrapper for malloc, free and calloc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include "wrap.h"
#include <libintl.h>
#include <locale.h>
#define _(STRING) gettext(STRING)
#define MSG_SIZE 1000
char msg[MSG_SIZE];
void * __real_malloc(size_t s);
void * __real_calloc(size_t nmemb, size_t s);
void __real_free(void *);
void * __real_realloc(void *ptr, size_t size);
extern bool wrap_monitoring;
extern struct wrap_stats_t stats;
extern struct wrap_monitor_t monitored;
extern struct wrap_fail_t failures;
extern struct wrap_log_t logs;
//
// keeps only MAX_LOG in memory
//
void log_malloc(void *ptr, size_t size) {
if(ptr!=NULL && logs.malloc.n < MAX_LOG) {
logs.malloc.log[logs.malloc.n].size=size;
logs.malloc.log[logs.malloc.n].ptr=ptr;
logs.malloc.n++;
}
}
void update_realloc_block(void *ptr, size_t newsize) {
for(int i=0;i<MAX_LOG;i++) {
if(logs.malloc.log[i].ptr==ptr) {
logs.malloc.log[i].size=newsize;
return;
}
}
return ;
}
size_t find_size_malloc(void *ptr) {
for(int i=0;i<MAX_LOG;i++) {
if(logs.malloc.log[i].ptr==ptr)
return logs.malloc.log[i].size;
}
return -1;
}
void * __wrap_malloc(size_t size) {
if(!wrap_monitoring || !monitored.malloc) {
return __real_malloc(size);
}
stats.malloc.called++;
stats.malloc.last_params.size=size;
if(FAIL(failures.malloc)) {
failures.malloc=NEXT(failures.malloc);
return failures.malloc_ret;
}
stats.memory.used+=size;
failures.malloc=NEXT(failures.malloc);
void *ptr=__real_malloc(size);
stats.malloc.last_return=ptr;
log_malloc(ptr,size);
return ptr;
}
void * __wrap_realloc(void *ptr, size_t size) {
if(!wrap_monitoring || !monitored.realloc) {
return __real_realloc(ptr, size);
}
stats.realloc.called++;
stats.realloc.last_params.size=size;
if(FAIL(failures.realloc)) {
failures.realloc=NEXT(failures.realloc);
return failures.realloc_ret;
}
failures.realloc=NEXT(failures.realloc);
int old_size=find_size_malloc(ptr);
void *r_ptr=__real_realloc(ptr,size);
stats.realloc.last_return=r_ptr;
if(ptr!=NULL) {
stats.memory.used+=size-old_size;
update_realloc_block(ptr,size);
}
return r_ptr;
}
void * __wrap_calloc(size_t nmemb, size_t size) {
if(!wrap_monitoring || !monitored.calloc) {
return __real_calloc(nmemb, size);
}
stats.calloc.called++;
stats.calloc.last_params.size=size;
stats.calloc.last_params.nmemb=nmemb;
if(FAIL(failures.calloc)) {
failures.calloc=NEXT(failures.calloc);
return failures.calloc_ret;
}
stats.memory.used+=nmemb*size;
failures.calloc=NEXT(failures.calloc);
void *ptr=__real_calloc(nmemb,size);
stats.calloc.last_return=ptr;
log_malloc(ptr,nmemb*size);
return ptr;
}
int malloc_free_ptr(void *ptr) {
for(int i=0;i<MAX_LOG;i++) {
if(logs.malloc.log[i].ptr==ptr) {
int size=logs.malloc.log[i].size;
logs.malloc.log[i].size=-1;
logs.malloc.log[i].ptr=NULL;
return size;
}
}
return 0;
}
void __wrap_free(void *ptr) {
if(!wrap_monitoring || !monitored.free) {
return __real_free(ptr);
}
stats.free.called++;
stats.free.last_params.ptr=ptr;
if(ptr!=NULL) {
stats.memory.used-=malloc_free_ptr(ptr);
if (FAIL(failures.free))
failures.free=NEXT(failures.free);
else
__real_free(ptr);
}
}
/*
void * find_ptr_malloc(size_t size){
for(int i=0;i<MAX_LOG;i++) {
if(logs.malloc.log[i].size==size)
return logs.malloc.log[i].ptr;
}
return NULL;
}
void malloc_log_init(struct malloc_t *l) {
for(int i=0;i<MAX_LOG;i++) {
l->log[i].size=-1;
l->log[i].ptr=NULL;
}
l->n=0;
}
*/
int malloc_allocated() {
int tot=0;
for(int i=0;i<MAX_LOG;i++) {
if(logs.malloc.log[i].ptr!=NULL) {
tot+=(int) logs.malloc.log[i].size;
}
}
return tot;
}
/*
* returns true if the address has been managed by malloc, false
* otherwise (also false if address has been freed)
*/
int malloced(void *addr) {
for(int i=0;i<logs.malloc.n;i++) {
if(logs.malloc.log[i].ptr<=addr &&
(logs.malloc.log[i].ptr+ logs.malloc.log[i].size)>=addr) {
return true;
}
}
return false;
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter