Skip to content
Snippets Groups Projects
Commit 105b53d0 authored by Andrei Stan's avatar Andrei Stan
Browse files

{checker,src}: Update checker and base code


Signed-off-by: default avatarAndrei Stan <andreistan2003@gmail.com>
parent dd28f1e1
No related branches found
No related tags found
No related merge requests found
......@@ -8,10 +8,6 @@ cp -r "$CHECKER_DATA_DIRECTORY"/../tests ..
# Everything happens in the tests/ directory.
pushd ../tests > /dev/null || exit 1
echo -e "\n\n### RUNING CHECKER\n\n"
make check
echo -e "\n\n### RUNING LINTER\n\n"
make lint
./grade.sh
popd > /dev/null || exit 1
......@@ -11,7 +11,7 @@
void consumer_thread(so_consumer_ctx_t *ctx)
{
/* TODO: implement consume threads */
/* TODO: implement consumer thread */
(void) ctx;
}
......
......@@ -8,6 +8,7 @@
#include "ring_buffer.h"
#include "consumer.h"
#include "producer.h"
#include "log/log.h"
#include "packet.h"
#include "utils.h"
......@@ -40,7 +41,7 @@ void __attribute__((destructor)) dest()
int main(int argc, char **argv)
{
so_ring_buffer_t ring_buffer;
int num_consumers, threads;
int num_consumers, threads, rc;
pthread_t *thread_ids = NULL;
if (argc < 4) {
......@@ -48,8 +49,7 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
int rc = ring_buffer_init(&ring_buffer, SO_RING_SZ);
rc = ring_buffer_init(&ring_buffer, SO_RING_SZ);
DIE(rc < 0, "ring_buffer_init");
num_consumers = strtol(argv[3], NULL, 10);
......@@ -60,7 +60,7 @@ int main(int argc, char **argv)
}
thread_ids = calloc(num_consumers, sizeof(pthread_t));
DIE(thread_ids == NULL, "malloc pthread_t");
DIE(thread_ids == NULL, "calloc pthread_t");
/* create consumer threads */
threads = create_consumers(thread_ids, num_consumers, &ring_buffer, argv[2]);
......
// SPDX-License-Identifier: BSD-3-Clause
/*
* Heavily inspired for Linux kernel code:
* https://github.com/torvalds/linux/blob/master/include/linux/list.h
*/
#ifndef __OS_LIST_H__
#define __OS_LIST_H__ 1
#include <stddef.h>
typedef struct os_list_node_t {
struct os_list_node_t *prev, *next;
} os_list_node_t;
static inline void list_init(os_list_node_t *head)
{
head->prev = head;
head->next = head;
}
static inline void list_add(os_list_node_t *head, os_list_node_t *node)
{
node->next = head->next;
node->prev = head;
head->next->prev = node;
head->next = node;
}
static inline void list_add_tail(os_list_node_t *head, os_list_node_t *node)
{
node->prev = head->prev;
node->next = head;
head->prev->next = node;
head->prev = node;
}
static inline void list_del(os_list_node_t *node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = node;
node->prev = node;
}
static inline int list_empty(os_list_node_t *head)
{
return (head->next == head);
}
#define list_entry(ptr, type, member) ({ \
void *tmp = (void *)(ptr); \
(type *) (tmp - offsetof(type, member)); \
})
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, tmp, head) \
for (pos = (head)->next, tmp = pos->next; pos != (head); \
pos = tmp, tmp = pos->next)
#endif
// SPDX-License-Identifier: BSD-3-Clause
#ifndef __SO_PACKET_H__
#define __SO_PACKET_H__
#include "list.h"
#define PKT_SZ 256
typedef enum {
......
// SPDX-License-Identifier: BSD-3-Clause
//
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
......
......@@ -4,7 +4,7 @@
int ring_buffer_init(so_ring_buffer_t *ring, size_t cap)
{
/* TODO: implement me */
/* TODO: implement ring_buffer_init */
(void) ring;
(void) cap;
......@@ -13,7 +13,7 @@ int ring_buffer_init(so_ring_buffer_t *ring, size_t cap)
ssize_t ring_buffer_enqueue(so_ring_buffer_t *ring, void *data, size_t size)
{
/* TODO: implement me */
/* TODO: implement ring_buffer_enqueue */
(void) ring;
(void) data;
(void) size;
......@@ -23,7 +23,7 @@ ssize_t ring_buffer_enqueue(so_ring_buffer_t *ring, void *data, size_t size)
ssize_t ring_buffer_dequeue(so_ring_buffer_t *ring, void *data, size_t size)
{
/* TODO: Implement me */
/* TODO: Implement ring_buffer_dequeue */
(void) ring;
(void) data;
(void) size;
......@@ -33,12 +33,12 @@ ssize_t ring_buffer_dequeue(so_ring_buffer_t *ring, void *data, size_t size)
void ring_buffer_destroy(so_ring_buffer_t *ring)
{
/* TODO: Implement me */
/* TODO: Implement ring_buffer_destroy */
(void) ring;
}
void ring_buffer_stop(so_ring_buffer_t *ring)
{
/* TODO: Implement me */
/* TODO: Implement ring_buffer_stop */
(void) ring;
}
......@@ -10,8 +10,11 @@ binary with the appropiate file from `out/` directory.
import os
import subprocess
import shutil
import time
import signal
from gen_packets import generate_packets
from typing import List
src = os.environ.get("SRC_PATH", "../src")
in_dir = os.path.join(os.curdir, "in")
......@@ -22,6 +25,12 @@ SORT_TIMEOUT = 1
CMP_TIMEOUT = 1
SERIAL_TIMEOUT = 5
TEST_SIZES = [10, 100, 1_000, 10_000, 20_000]
"""Two millisecond for each probe"""
TRACE_LOG_TIMESLICE = 1 / 500
TOTAL = 0.0
GEN_TEST_ERROR_MSG="""Generating test cases for the checker failed. Please run:
......@@ -87,7 +96,8 @@ def generate_tests(test_sizes) -> bool:
print_log("Tests cases generated")
return True
def files_are_identical(out_file: str, ref_file: str, sort_out_file: bool) -> bool:
def files_are_identical(out_file: str, ref_file: str,
sort_out_file: bool) -> bool:
""" Return true if the files are identical with the option of sorting the
output file before comaparing the two.
"""
......@@ -100,8 +110,7 @@ def files_are_identical(out_file: str, ref_file: str, sort_out_file: bool) -> bo
except subprocess.TimeoutExpired:
sort_proc.kill()
return False
with subprocess.Popen(["cmp", "-s", out_file, ref_file]) as cmp_proc:
with subprocess.Popen(["cmp", "-s", out_file, ref_file]) as cmp_proc:
try:
return cmp_proc.wait(timeout=CMP_TIMEOUT) == 0
except subprocess.TimeoutExpired:
......@@ -110,14 +119,41 @@ def files_are_identical(out_file: str, ref_file: str, sort_out_file: bool) -> bo
return False
def file_prefix_identical(out_file_path: str, ref_file_path: str) -> bool:
"""Check if the output file is a prefix of the ref file.
Used to check during the runtime of the firewall if the data is
inserted in a sorted order."""
with open(out_file_path) as out_file, open(ref_file_path) as ref_file:
for out_line in out_file:
ref_line = ref_file.readline()
if out_line != ref_line:
return False
return True
def file_line_count(file_path: str) -> int:
return sum(1 for _ in open(file_path))
def line_count_probe_check(probe: List[int]) -> bool:
double_occurences = 0
for i in range(1, len(probe)):
if probe[i] == probe[i - 1] and probe[i] != 0 and probe[i] not in TEST_SIZES:
double_occurences += 1
return double_occurences / len(probe) <= 0.5
def file_exists_probe_check(probe: List[bool]) -> bool:
return any(probe)
def run_once_and_check_output(binary: str, in_file_path: str, out_file_path: str,
ref_file_path: str, sort_out_file: bool,
threads: int = 1, test_timeout: float = 1) -> bool:
threads: int = 1, test_timeout: float = 1,
trace_logs: bool = False) -> bool:
"""Run the test once and check the output file with the reference file
Also, delete the output file before running to ensure all the content
was written during this run.
"""
was written during this run."""
# Delete output file
try:
os.remove(out_file_path)
......@@ -125,11 +161,46 @@ def run_once_and_check_output(binary: str, in_file_path: str, out_file_path: str
pass
with subprocess.Popen([binary, in_file_path, out_file_path, f'{threads}'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc_firewall_res:
stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) as proc_firewall_res:
try:
# For now ignore both stdout and stderr of process
# TODO: log the stderr / stdout to a file
_, _ = proc_firewall_res.communicate(timeout=test_timeout)
"""Check periodically if the output file is sorted correctly"""
if trace_logs:
err = ""
line_count_probe, file_exists_probe = [], []
running_time = 0
while running_time < test_timeout and \
proc_firewall_res.poll() is None:
time.sleep(TRACE_LOG_TIMESLICE)
running_time += TRACE_LOG_TIMESLICE
file_exists = os.path.isfile(out_file_path)
file_exists_probe.append(file_exists)
if file_exists:
proc_firewall_res.send_signal(signal.SIGSTOP)
line_count_probe.append(file_line_count(out_file_path))
if not file_prefix_identical(out_file_path, ref_file_path):
err = f"Log tracing failed: file {ref_file_path} does not" + \
f"start with the contents of {out_file_path}."
break
proc_firewall_res.send_signal(signal.SIGCONT)
if running_time >= test_timeout:
err = "Time expired for process!! Killing ..."
if not line_count_probe_check(line_count_probe):
err = "Line count did not change between two probes too many times"
if not file_exists_probe_check(file_exists_probe):
err = "File was never created during execution"
if err != "":
print(err)
proc_firewall_res.kill()
return False
else:
_, _ = proc_firewall_res.communicate(timeout=test_timeout)
except subprocess.TimeoutExpired:
print("Time expired for process!! Killing ...")
proc_firewall_res.kill()
......@@ -142,7 +213,8 @@ def run_once_and_check_output(binary: str, in_file_path: str, out_file_path: str
return files_are_identical(out_file_path, ref_file_path, sort_out_file)
def check(test_name: str, sort_out_file: bool, threads: int,
test_timeout: float = 1, number_of_runs: int = 1):
test_timeout: float = 1, number_of_runs: int = 1,
trace_logs: bool = False):
"""Check a test file.
Pass test filenames `firewall`
......@@ -172,16 +244,24 @@ def check(test_name: str, sort_out_file: bool, threads: int,
for _ in range(number_of_runs):
if not run_once_and_check_output(firewall_path, in_file_path,
out_file_path, ref_file_path,
sort_out_file,
True,
threads=threads,
test_timeout=test_timeout):
return False
if trace_logs:
if not run_once_and_check_output(firewall_path, in_file_path,
out_file_path, ref_file_path,
sort_out_file,
threads=threads,
test_timeout=test_timeout,
trace_logs=trace_logs):
return False
return True
def check_and_grade(test_size: int, sort_out_file: bool = False,
threads: int = 1, test_timeout: int = 2,
points: float = 5.0):
points: float = 5.0, trace_logs: bool = False):
"""Check and grade a single test case using test size and number of threads
"""
......@@ -194,7 +274,7 @@ def check_and_grade(test_size: int, sort_out_file: bool = False,
"s" if threads > 1 else " ")
result_format += " " + 22 * "." + " "
if check(test_name, sort_out_file, threads, test_timeout, 20):
if check(test_name, sort_out_file, threads, test_timeout, 20, trace_logs):
print(result_format + "passed ... {}".format(points))
TOTAL += points
else:
......@@ -203,9 +283,7 @@ def check_and_grade(test_size: int, sort_out_file: bool = False,
def main():
global TOTAL
test_sizes = [10, 100, 1_000, 10_000, 25_000]
if not generate_tests(test_sizes):
if not generate_tests(TEST_SIZES):
# Something bad happened, can't run checker, try again
return
......@@ -214,20 +292,21 @@ def main():
print_log("Running tests ...", newline=True)
# Test out serial implementation: 10 points.
check_and_grade(test_sizes[0], threads=1, points=3)
check_and_grade(test_sizes[2], threads=1, points=3)
check_and_grade(test_sizes[4], threads=1, points=4)
check_and_grade(TEST_SIZES[0], threads=1, points=3)
check_and_grade(TEST_SIZES[2], threads=1, points=3)
check_and_grade(TEST_SIZES[4], threads=1, points=4)
# Test out parallel implementation, but without the restriction of having
# correctly sorted output: 50 points (2 x 5 x 5).
for test_size in test_sizes:
for test_size in TEST_SIZES:
check_and_grade(test_size, threads=2, sort_out_file=True, points=5)
check_and_grade(test_size, threads=4, sort_out_file=True, points=5)
# Test out parallel implementation, this time with the restriction of having
# correctly sored output: 30 points (5 x 6)
for test_size in test_sizes:
check_and_grade(test_size, threads=4, sort_out_file=False, points=6)
for test_size in TEST_SIZES[2:]:
check_and_grade(test_size, threads=4, sort_out_file=False, points=5, trace_logs=True)
check_and_grade(test_size, threads=8, sort_out_file=False, points=5, trace_logs=True)
TOTAL = int(TOTAL)
print("\nTotal:" + 67 * " " + f" {TOTAL}/100")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment