diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c6b186b092255511301ba4fcf62f9e72ac950089..7dc8f3e160bbe9d03a6a0f3ebf6dc039cda2287b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,6 +27,6 @@ build: checker: stage: test image: - name: gitlab.cs.pub.ro:5050/so2/0-list + name: gitlab.cs.pub.ro:5050/so2/1-tracer script: - ./checker/checker.sh $ASSIGNMENT diff --git a/Dockerfile b/Dockerfile index e77297bff1fa547a66c9de764ad886e6ee915f18..ef39da2c78dd36360300f8f8edf3d36e020def43 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ FROM gitlab.cs.pub.ro:5050/so2/so2-assignments RUN echo "Hello from Docker" -RUN mkdir -p /linux/tools/labs/skels/assignments/0-list -RUN mkdir -p /linux/tools/labs/skels/assignments/0-list-checker +RUN mkdir -p /linux/tools/labs/skels/assignments/1-tracer +RUN mkdir -p /linux/tools/labs/skels/assignments/1-tracer-checker -COPY ./checker/0-list-checker /linux/tools/labs/skels/assignments/0-list-checker +COPY ./checker/1-tracer-checker /linux/tools/labs/skels/assignments/1-tracer-checker COPY ./checker/checker_daemons/so2_vm_checker_daemon.sh /linux/tools/labs/rootfs/etc/init.d RUN chmod +x /linux/tools/labs/rootfs/etc/init.d/so2_vm_checker_daemon.sh diff --git a/checker/1-tracer-checker/Makefile b/checker/1-tracer-checker/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ed86666ec4df3c717a64d5951aeefbcfdbef826d --- /dev/null +++ b/checker/1-tracer-checker/Makefile @@ -0,0 +1,19 @@ +LDFLAGS = -m32 + +.PHONY: all clean + +all: tracer_test + +tracer_test: tracer_test.o + +tracer_test.o: _test/tracer_test.c + make -C _test + ln -sfn _test/tracer_test.o $@ + +run: all + ./_checker + +clean: + -rm -f *~ + -rm -f tracer_test.o tracer_test + -make -C _test clean diff --git a/checker/1-tracer-checker/README b/checker/1-tracer-checker/README new file mode 100644 index 0000000000000000000000000000000000000000..75c90417a94fb66d95644b7321a7d6adf0c0630f --- /dev/null +++ b/checker/1-tracer-checker/README @@ -0,0 +1,113 @@ += KPROBE BASED TRACER TEST SUITE == + +Test suite for kprobe based tracer + +== FILES == + +README + * this file + +Makefile + * Makefile for automating the build process + +_checker + * script to run all tests defined in _test/tracer_test.c + +_test/Makefile + * test suite internal Makefile (creates necessary object files) + +_test/tracer_test.c + * test suite for Kprobe Based Tracer + +_test/tracer_test.h + * test suite header file + +_test/tracer.h + * kprobe tracer header file (macros and structures) + +_test/test.h + * useful macros for testing + +_test/debug.h + * debugging macros + +_test/util.h + * useful macros for generic use (error processing) + +_helper/* + * helper kernel module for simulating kprobed ops + +== BUILDING == + +Use the linux-kernel-labs build infrastructure, i.e run + + make build + +in the tools/labs/ folder. + +If you want to do things by hand, you have to use the Makefile in the +current folder to run build the executable: + + make + +and the kernel module-specific Makefile command to build the kernel module: + +make -C $(KDIR) M=$(KDIR)/tools/labs/skels/assignments/1-tracer/checker/_helper/ ARCH=x86 modules + +== RUNNING == + +Use the linux-kernel-labs run infrastructure, i.e. run + + make copy + +This copies the skel/ subfolder in the future root filesystem image for the +QEMU/KVM virtual machine. + +If you want to do things by hand, copy your tracer.ko module and _checker, +tracer_test and tracer_helper.ko to fsimg/root directory on your QEMU/KVM +virtual machine. + +In order to run the test suite you can either use the _checker +script or run the tracer_test executable. + +The _checker script runs all tests and computes assignment grade: + + ./_checker + +In order to run a specific test pass the test number (1 .. 10) to the +tracer_test executable. + + ./tracer_test 5 + +== TESTS == + +Tests are basically unit tests. A single function in the test_fun_array (see +tracer_test.c) is called each time the tracer_test executable is invoked, +testing a single functionality (and assuming previous tests have been run and +passed). + +The EXIT_IF_FAIL macro (see test.h) is unnecessary since after each test, the +program completes. + +Each test function follows the unit test pattern: initialization, action, +evaluation. The test macro (see test.h) is invoked at the end of each test +for evaluating and grading the test. + +== DEBUGGING == + +The debug.h header file consists of several macros useful for debugging +(dprintf, dlog). There are multiple uses of these macros throughout the above +files. + +In order to turn debug messages on, you must define the DEBUG macro, either in +a header file, or, I suggest, in the Makefile. The LOG_LEVEL macro limits the +log message types that are to be printed, by default LOG_WARNING (see enum in +debug.h). You may redefine it in a header file or in the Makefile. + +Rapid enabling of debug messages is achieved by commenting out the CPPFLAGS +line in the Makefile. It turns on debugging and enables all log messages +(LOG_DEBUG). + +== OTHER == + +srand48() and drand48() are used for generating random numbers. diff --git a/checker/1-tracer-checker/_checker b/checker/1-tracer-checker/_checker new file mode 100644 index 0000000000000000000000000000000000000000..f5a4a7da1b5d2636d6f1d0fa6b0c5f1638899aa0 --- /dev/null +++ b/checker/1-tracer-checker/_checker @@ -0,0 +1,24 @@ +#!/bin/sh + +first_test=1 +last_test=10 +executable=tracer_test + +for i in $(seq $first_test $last_test); do + ./"$executable" $i +done | tee results.txt + +cat results.txt | grep '\[.*\]$' | awk -F '[] /[]+' ' +BEGIN { + sum=0 +} + +{ + sum += $2; +} + +END { + printf "\n%66s [%03d/100]\n", "Total:", sum; +}' + +rm -f results.txt diff --git a/checker/1-tracer-checker/_helper/Kbuild b/checker/1-tracer-checker/_helper/Kbuild new file mode 100644 index 0000000000000000000000000000000000000000..7a7b493cfd9a0f51eb37e6a7f2edb5dfcb15d895 --- /dev/null +++ b/checker/1-tracer-checker/_helper/Kbuild @@ -0,0 +1,4 @@ +EXTRA_CFLAGS = -Wall -g + +obj-m = tracer_helper.o + diff --git a/checker/1-tracer-checker/_helper/helper.h b/checker/1-tracer-checker/_helper/helper.h new file mode 100644 index 0000000000000000000000000000000000000000..3cb30e03c9dcb9cf8b37bba858125ca1b9cfa496 --- /dev/null +++ b/checker/1-tracer-checker/_helper/helper.h @@ -0,0 +1,35 @@ +#ifndef _HELPER__ +#define _HELPER__ + +#include <asm/ioctl.h> + +#define NAMESIZE 64 +#define MCOUNT 128 + +#define PREPARE_TEST _IOW('t', 19, unsigned int) +#define START_TEST _IOW('t', 20, unsigned int) +#define STOP_TEST _IOW('t', 21, unsigned int) + +/*XXX match test_params with tracers_stats + * perhaps use the same struct + */ +struct test_params { + pid_t pid; + char thread_name[NAMESIZE]; + int idx; /* index for multi-kthreaded test */ + /* + * kcalls: 5 + * alloc : [1024] [8] [128] [10] [128] + * free : [0] [0] [1] [0] [1] + */ + int kcalls; /* number of kmalloc calls */ + int alloc[MCOUNT]; /* sizes of kmalloc allocations */ + int free[MCOUNT]; /* intmap for which allocations to free */ + int sched; + int up; + int down; + int lock; + int unlock; +}; + +#endif diff --git a/checker/1-tracer-checker/_helper/tracer_helper.c b/checker/1-tracer-checker/_helper/tracer_helper.c new file mode 100644 index 0000000000000000000000000000000000000000..1c93d230c455111ab3df7eb4a8a2ee25f4592701 --- /dev/null +++ b/checker/1-tracer-checker/_helper/tracer_helper.c @@ -0,0 +1,201 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/semaphore.h> +#include <linux/mutex.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <asm/ioctl.h> +#include <linux/uaccess.h> +#include <linux/semaphore.h> +#include <linux/mutex.h> + +#include "helper.h" + + +#define HELPER_MINOR 30 + +/* number of paralel kernel threads */ +static wait_queue_head_t wq[MCOUNT]; + +/*XXX: this looks like a poor design, please refactor */ +static int thread_prepared[MCOUNT] = {0, }; +static int thread_running[MCOUNT] = {0, }; +static int thread_should_stop[MCOUNT] = {0, }; +static struct task_struct *t[MCOUNT] = {NULL, }; + +static struct test_params tp[MCOUNT]; +static int tcount; + +void do_work(void) +{ + int i, j; + int a = 0; + + for (i = 0; i < 1000; i++) + for (j = 0; j < 1000; j++) + a = i * j; +} + +int thread_fn(void *data) +{ + int i; + + void *k_addr[MCOUNT]; + struct semaphore sem; + struct mutex lock; + + struct test_params *tp; + + tp = (struct test_params *)data; + + thread_prepared[tp->idx] = 1; + wake_up_interruptible(&wq[tp->idx]); + + wait_event_interruptible(wq[tp->idx], thread_running[tp->idx] == 1); + + for (i = 0; i < tp->kcalls; i++) + k_addr[i] = kmalloc(tp->alloc[i], GFP_KERNEL); + + /*XXX: do proper cleanup, avoid memory leaks */ + for (i = 0; i < tp->kcalls; i++) + if (tp->free[i] && k_addr[i]) + kfree(k_addr[i]); + + for (i = 0; i < tp->sched; i++) + schedule(); + + /* ***: use tp->down for down_interruptible */ + sema_init(&sem, 1); + for (i = 0; i < tp->up; i++) { + up(&sem); + do_work(); + down_interruptible(&sem); + } + /* ***: use to->unlock for mutex_unlock */ + mutex_init(&lock); + for (i = 0; i < tp->lock; i++) { + mutex_lock(&lock); + do_work(); + mutex_unlock(&lock); + } + + wait_event_interruptible(wq[tp->idx], thread_should_stop[tp->idx] == 1); + + /* reset state machine */ + thread_prepared[tp->idx] = 0; + thread_running[tp->idx] = 0; + thread_should_stop[tp->idx] = 0; + + return 0; +} +static int helper_open(struct inode *inode, struct file *file) +{ +#ifdef DEBUG + pr_info("tracer-helper: open\n"); +#endif + return 0; +} + +static int helper_release(struct inode *inode, struct file *file) +{ +#ifdef DEBUG + pr_info("tracer-helper: close\n"); +#endif + return 0; +} + +static long helper_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case PREPARE_TEST: + if (copy_from_user(&tp[tcount], (struct test_param *)arg, + sizeof(tp[tcount]))) { + pr_info("Error copy to user\n"); + return -EFAULT; + } + t[tp[tcount].idx] = kthread_run(thread_fn, &tp[tcount], "%s", + tp[tcount].thread_name); + if (!t[tp[tcount].idx]) { + pr_info("Could not create thread!\n"); + return -ENOMEM; + } + + ret = t[tp[tcount].idx]->pid; + wait_event_interruptible(wq[tp[tcount].idx], + thread_prepared[tp[tcount].idx] == 1); + tcount++; + break; + case START_TEST: +#if 0 + pr_info("%s: start test for idx %lu\n", __func__, arg); +#endif + thread_running[arg] = 1; + wake_up_interruptible(&wq[arg]); + break; + case STOP_TEST: +#if 0 + pr_info("%s: stop test for idx %lu\n", __func__, arg); +#endif + thread_should_stop[arg] = 1; + wake_up_interruptible(&wq[arg]); + kthread_stop(t[arg]); + break; + default: + break; + } + + return ret; +} + +static const struct file_operations tracer_fops = { + .open = helper_open, + .unlocked_ioctl = helper_ioctl, + .release = helper_release, +}; + +static struct miscdevice helper_dev = { + .minor = HELPER_MINOR, + .name = "helper", + .fops = &tracer_fops, +}; + +static int __init tracer_helper_init(void) +{ + int rc, i; + + rc = misc_register(&helper_dev); + if (rc < 0) { + pr_err("misc_register: fail\n"); + return rc; + } + + for (i = 0; i < MCOUNT; i++) + init_waitqueue_head(&wq[i]); +#ifdef DEBUG + pr_info("tracer-helper: init\n"); +#endif + return 0; +} + +static void __exit tracer_helper_exit(void) +{ + misc_deregister(&helper_dev); +#ifdef DEBUG + pr_info("tracer-helper: exit\n"); +#endif +} + +MODULE_AUTHOR("Daniel Baluta"); +MODULE_LICENSE("GPL"); + +module_init(tracer_helper_init); +module_exit(tracer_helper_exit); diff --git a/checker/1-tracer-checker/_test/Makefile b/checker/1-tracer-checker/_test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..00160bc9b3647c676368dc94e14df8ccc707d554 --- /dev/null +++ b/checker/1-tracer-checker/_test/Makefile @@ -0,0 +1,10 @@ +#CPPFLAGS = -DDEBUG -DLOG_LEVEL=LOG_DEBUG +CFLAGS = -Wall -g -m32 + +.PHONY: all clean + +all: tracer_test.o + +clean: + -rm -f *~ + -rm -f tracer_test.o diff --git a/checker/1-tracer-checker/_test/debug.h b/checker/1-tracer-checker/_test/debug.h new file mode 100644 index 0000000000000000000000000000000000000000..debdeccf492038eb258619d20e0cd681a009506b --- /dev/null +++ b/checker/1-tracer-checker/_test/debug.h @@ -0,0 +1,77 @@ +/* + * debugging macros + * heavily inspired by previous work and Internet resources + * + * uses C99 variadic macros + * uses non-standard usage of the token-paste operator (##) for + * removing the comma symbol (,) when not followed by a token + * uses non-standard __FUNCTION__ macro (MSVC doesn't support __func__) + * tested on gcc 4.4.5 and Visual Studio 2008 (9.0), compiler version 15.00 + * + * 2011, Razvan Deaconescu, razvan.deaconescu@cs.pub.ro + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +/* log levels */ +enum { + LOG_EMERG = 1, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG +}; + +/* + * initialize default loglevel (for dlog) + * may be redefined in the including code + */ + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_WARNING +#endif + +/* + * define DEBUG macro as a compiler option: + * -DDEBUG for GCC + * /DDEBUG for MSVC + */ + +#if defined DEBUG +#define dprintf(format, ...) \ + fprintf(stderr, " [%s(), %s:%u] " format, \ + __func__, __FILE__, __LINE__, \ + ##__VA_ARGS__) +#else +#define dprintf(format, ...) \ + do { \ + } while (0) +#endif + +#if defined DEBUG +#define dlog(level, format, ...) \ + do { \ + if (level <= LOG_LEVEL) \ + dprintf(format, ##__VA_ARGS__); \ + } while (0) +#else +#define dlog(level, format, ...) \ + do { \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/1-tracer-checker/_test/helper.h b/checker/1-tracer-checker/_test/helper.h new file mode 100644 index 0000000000000000000000000000000000000000..3cb30e03c9dcb9cf8b37bba858125ca1b9cfa496 --- /dev/null +++ b/checker/1-tracer-checker/_test/helper.h @@ -0,0 +1,35 @@ +#ifndef _HELPER__ +#define _HELPER__ + +#include <asm/ioctl.h> + +#define NAMESIZE 64 +#define MCOUNT 128 + +#define PREPARE_TEST _IOW('t', 19, unsigned int) +#define START_TEST _IOW('t', 20, unsigned int) +#define STOP_TEST _IOW('t', 21, unsigned int) + +/*XXX match test_params with tracers_stats + * perhaps use the same struct + */ +struct test_params { + pid_t pid; + char thread_name[NAMESIZE]; + int idx; /* index for multi-kthreaded test */ + /* + * kcalls: 5 + * alloc : [1024] [8] [128] [10] [128] + * free : [0] [0] [1] [0] [1] + */ + int kcalls; /* number of kmalloc calls */ + int alloc[MCOUNT]; /* sizes of kmalloc allocations */ + int free[MCOUNT]; /* intmap for which allocations to free */ + int sched; + int up; + int down; + int lock; + int unlock; +}; + +#endif diff --git a/checker/1-tracer-checker/_test/test.h b/checker/1-tracer-checker/_test/test.h new file mode 100644 index 0000000000000000000000000000000000000000..49ba56f0eeb5251239b6fb60e1978a12e789578d --- /dev/null +++ b/checker/1-tracer-checker/_test/test.h @@ -0,0 +1,63 @@ +/* + * generic test suite + * + * test macros and headers + */ + +#ifndef TEST_H_ +#define TEST_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +/* to be defined by calling program */ +extern int max_points; + +/* + * uncommend EXIT_IF_FAIL macro in order to stop test execution + * at first failed test + */ + +/*#define EXIT_IF_FAIL 1*/ + +#if defined(EXIT_IF_FAIL) +#define test_do_fail(points) \ + do { \ + printf("failed\n"); \ + exit(EXIT_FAILURE); \ + } while (0) +#else +#define test_do_fail(points) \ + printf("failed [ 0/%3d]\n", max_points) +#endif + +#define test_do_pass(points) \ + printf("passed [%3d/%3d]\n", points, max_points) + +#define test(message, test, points) \ + do { \ + size_t _i; \ + int t = (test); \ + \ + printf("%s", message); \ + fflush(stdout); \ + \ + for (_i = 0; _i < 60 - strlen(message); _i++) \ + putchar('.'); \ + \ + if (!t) \ + test_do_fail(points); \ + else \ + test_do_pass(points); \ + \ + fflush(stdout); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/1-tracer-checker/_test/tracer.h b/checker/1-tracer-checker/_test/tracer.h new file mode 100644 index 0000000000000000000000000000000000000000..7a55257b770aaf29bfbc2f44186dd466a48f74f7 --- /dev/null +++ b/checker/1-tracer-checker/_test/tracer.h @@ -0,0 +1,21 @@ +/* + * SO2 kprobe based tracer header file + * + * this is shared with user space + */ + +#ifndef TRACER_H__ +#define TRACER_H__ 1 + +#include <asm/ioctl.h> +#ifndef __KERNEL__ +#include <sys/types.h> +#endif /* __KERNEL__ */ + +#define TRACER_DEV_MINOR 42 +#define TRACER_DEV_NAME "tracer" + +#define TRACER_ADD_PROCESS _IOW(_IOC_WRITE, 42, pid_t) +#define TRACER_REMOVE_PROCESS _IOW(_IOC_WRITE, 43, pid_t) + +#endif /* TRACER_H_ */ diff --git a/checker/1-tracer-checker/_test/tracer_test.c b/checker/1-tracer-checker/_test/tracer_test.c new file mode 100644 index 0000000000000000000000000000000000000000..175ebfa07caff5bdc855016d54842f39987519db --- /dev/null +++ b/checker/1-tracer-checker/_test/tracer_test.c @@ -0,0 +1,779 @@ +/* + * SO2 Kprobe based tracer - test suite + * + * Authors: + * Daniel Baluta <daniel.baluta@gmail.com> + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> +#include <assert.h> +#include <fcntl.h> +#include <time.h> +#include <sys/ioctl.h> + + +#include "test.h" +#include "debug.h" +#include "util.h" + +#include "tracer.h" +#include "tracer_test.h" +#include "helper.h" + +/* use this to enable stats debugging */ +#if 0 +#define DEBUG +#endif + +#define MSECS 1000 + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct tracer_stats { + pid_t tr_pid; + int tr_alloc; + int tr_free; + int tr_mem; + int tr_mem_free; + int tr_sched; + int tr_up; + int tr_down; + int tr_lock; + int tr_unlock; +}; + +struct test_case { + char test_name[NAMESIZE]; + int score; + struct test_params test_params; +}; + +struct tracer_stats ts[MCOUNT]; + +struct test_case tc[] = { + /* 0 */ + { + .test_name = "test_simple_kmalloc", + .test_params = { + .thread_name = "xthread-0", + .kcalls = 1, + .alloc = {1024, }, + .idx = 0, + }, + .score = 5, + }, + /* 1 */ + { + .test_name = "test_simple_kfree", + .test_params = { + .thread_name = "xthread-1", + .kcalls = 1, + .alloc = {4096, }, + .free = {1, }, + .idx = 1, + }, + .score = 5, + }, + /* 2 */ + { + .test_name = "test_simple_sched", + .test_params = { + .thread_name = "xthread-2", + .sched = 1, + .idx = 2, + }, + .score = 4, + }, + /* 3 */ + { + .test_name = "test_simple_up_down", + .test_params = { + .thread_name = "xthread-3", + .up = 1, + .down = 1, + .idx = 3, + }, + .score = 4, + }, + /* 4 */ + { + .test_name = "test_simple_lock_unlock", + .test_params = { + .thread_name = "xthread-4", + .lock = 1, + .unlock = 1, + .idx = 4, + }, + .score = 4, + }, + + /* 5 */ + { + .test_name = "test_medium_kmalloc", + .test_params = { + .thread_name = "xthread-5", + .kcalls = 16, + .alloc = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + .idx = 5, + }, + .score = 5, + }, + + /* 6 */ + { + .test_name = "test_medium_free", + .test_params = { + .thread_name = "xthread-6", + .kcalls = 12, + .alloc = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}, + .free = {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1}, + .idx = 6, + }, + .score = 5, + }, + + /* 7 */ + { + .test_name = "test_medium_sched", + .test_params = { + .thread_name = "xthread-7", + .sched = 30, + .idx = 7, + }, + .score = 5, + }, + + /* 8 */ + { + .test_name = "test_medium_up_down", + .test_params = { + .thread_name = "xthread-8", + .up = 32, + .down = 32, + .idx = 8, + }, + .score = 4, + }, + /* 9 */ + { + .test_name = "test_medium_lock_unlock", + .test_params = { + .thread_name = "xthread-9", + .lock = 32, + .unlock = 32, + .idx = 9, + }, + .score = 4, + }, + /* 10 */ + { + .test_name = "test_medium_combined", + .test_params = { + .thread_name = "xthread-9", + .kcalls = 9, + .alloc = {1024, 512, 128, 64, 32, 64, 128, 512, 1024}, + .free = {1, 1, 1, 1, 1, 1, 1, 1, 1}, + .lock = 8, + .unlock = 8, + .up = 12, + .down = 12, + .idx = 10, + }, + .score = 5, + }, +}; + +/* declared in test.h; used for printing information in test macro */ +int max_points = 100; + +/* + * Do initialization for tracer test functions. + */ + +static void init_test(void) +{ + int rc; + + rc = system("insmod " MODULE_FILENAME); + DIE(rc != 0, "init_test"); +} + +static void init_test2(int *fd) +{ + int rc; + + system("insmod " MODULE_FILENAME); + + rc = open("/dev/tracer", O_RDONLY); + DIE(rc < 0, "init_test2"); + + *fd = rc; +} + +/* + * Do cleanup for tracer test functions. + */ + +static void cleanup_test(void) +{ + system("rmmod " MODULE_NAME); +} + +static void cleanup_test2(int fd) +{ + close(fd); + + system("rmmod " MODULE_NAME); +} + +/* + * Do initialization for tracer helper test module + */ +static void init_helper(int *fd) +{ + int rc; + + system("insmod " HELPER_MODULE_FILENAME); + + rc = open("/dev/helper", O_RDONLY); + DIE(rc < 0, "init helper"); + + *fd = rc; +} + +/* + * Do cleanup for tracer helper test module + */ + +static void cleanup_helper(int fd) +{ + close(fd); + + system("rmmod " HELPER_MODULE_NAME); +} + + +/* + * Check for successful module insertion and removal from the kernel. + */ + +static void test_insmod_rmmod(void) +{ + int rc; + + rc = system("insmod " MODULE_FILENAME); + test("test_insmod", rc == 0, 1); + + rc = system("rmmod " MODULE_NAME); + test("test_rmmod", rc == 0, 1); + + rc = system("insmod " MODULE_FILENAME); + test(__func__, rc == 0, 1); + + system("rmmod " MODULE_NAME); +} + +static void test_open_dev_tracer(void) +{ + int rc; + char dev_name[64]; + + init_test(); + snprintf(dev_name, 63, "/dev/%s", TRACER_DEV_NAME); + + rc = open(dev_name, O_RDONLY); + test(__func__, rc >= 0, 1); + close(rc); + + cleanup_test(); +} + +static void test_dev_minor_major(void) +{ + int rc; + struct stat buf; + + init_test(); + + rc = lstat("/dev/tracer", &buf); + if (rc < 0) { + perror("lstat"); + exit(-1); + } + test(__func__, major(buf.st_rdev) == 10 && + minor(buf.st_rdev) == 42, 1); + + cleanup_test(); +} + +/* + * Check for proc entry for kprobe stats + */ + +static void test_proc_entry_exists_after_insmod(void) +{ + int rc; + + init_test(); + + rc = system("ls /proc/tracer > /dev/null 2>&1"); + test(__func__, rc == 0, 2); + + cleanup_test(); +} + +static void test_proc_entry_inexistent_after_rmmod(void) +{ + int rc; + + init_test(); + cleanup_test(); + + rc = system("ls /proc/tracer > /dev/null 2>&1"); + test(__func__, rc != 0, 2); +} + +int tracer_proc_check_values(struct tracer_stats *st, + struct test_case *tc, int no) +{ + int idx, idz, idk;/* really? */ + int a, b, c, d, e, f, g, h, i, j;/* no, no */ + int total_mem = 0; + int total_free = 0; + int no_free = 0; + int ok = 0; + /* this is embarassing - O(n^2) - stats are not sorted by pid */ + + for (idx = 0; idx < no; idx++) { + ok = 0; + for (idk = 0; idk < no; idk++) { + if (st[idk].tr_pid != tc[idx].test_params.pid) + continue; + ok = 1; + total_mem = 0; + total_free = 0; + no_free = 0; + + for (idz = 0; idz < tc[idx].test_params.kcalls; idz++) { + total_mem += tc[idx].test_params.alloc[idz]; + total_free += tc[idx].test_params.free[idz] * + tc[idx].test_params.alloc[idz]; + if (tc[idx].test_params.free[idz]) + no_free++; + } + + a = (st[idk].tr_pid == tc[idx].test_params.pid); + b = (st[idk].tr_alloc == tc[idx].test_params.kcalls); + dprintf("tr_alloc (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_alloc, + tc[idx].test_params.kcalls); + + c = (st[idk].tr_free == no_free); + dprintf("tr_free (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_free, no_free); + + d = (st[idk].tr_mem == total_mem); + dprintf("tr_mem (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_mem, total_mem); + + e = (st[idk].tr_mem_free == total_free); + dprintf("tr_free (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_mem_free, + total_free); + + f = (st[idk].tr_sched >= tc[idx].test_params.sched); + dprintf("tr_sched (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_sched, + tc[idx].test_params.sched); + + g = (st[idk].tr_up == tc[idx].test_params.up); + dprintf("tr_up (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_up, + tc[idx].test_params.up); + + h = (st[idk].tr_down == tc[idx].test_params.down); + dprintf("tr_down (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_down, + tc[idx].test_params.down); + + i = (st[idk].tr_lock == tc[idx].test_params.lock); + dprintf("tr_lock (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_lock, + tc[idx].test_params.lock); + + j = (st[idk].tr_unlock == tc[idx].test_params.unlock); + dprintf("tr_unlock (%d): got %d, expected %d\n", + st[idk].tr_pid, st[idk].tr_unlock, + tc[idx].test_params.unlock); + + if (!a || !b || !c || !d || + !e || !f || !g || !h || !i || !j) + return 0; + } + } + return ok; +} + +static void tracer_proc_read_values(struct tracer_stats *st, int no) +{ + char buffer[256]; + FILE *f; + int i; + + f = fopen("/proc/tracer", "rt"); + DIE(f == NULL, "tracer_proc_read_value"); + + /* skip header line */ + fgets(buffer, 256, f); + + for (i = 0; i < no; i++) { + fscanf(f, "%d %d %d %d %d %d %d %d %d %d", + &st[i].tr_pid, &st[i].tr_alloc, &st[i].tr_free, + &st[i].tr_mem, &st[i].tr_mem_free, &st[i].tr_sched, + &st[i].tr_up, &st[i].tr_down, &st[i].tr_lock, + &st[i].tr_unlock); + } + fclose(f); +} + +/* + * creates a process prepared to run with @tp params + * returns the pid of the newly created process + */ +void prepare_helper(int fd, struct test_params *tp, pid_t *pid) +{ + int rc; + + rc = ioctl(fd, PREPARE_TEST, tp); + DIE(rc < 0, "prepare helper"); + *pid = rc; +} + +void start_helper(int fd, int idx) +{ + int rc; + + rc = ioctl(fd, START_TEST, idx); + DIE(rc < 0, "start helper"); +} + +void stop_helper(int fd, int idx) +{ + int rc; + + rc = ioctl(fd, STOP_TEST, idx); + DIE(rc < 0, "stop helper"); +} + +/* XXX: we should really check the return codes */ +void trace_process(int fd, pid_t pid) +{ + int rc; + + rc = ioctl(fd, TRACER_ADD_PROCESS, pid); + DIE(rc < 0, "trace_process"); +} + +void untrace_process(int fd, pid_t pid) +{ + int rc; + + rc = ioctl(fd, TRACER_REMOVE_PROCESS, pid); + DIE(rc < 0, "untrace process"); +} +static void test_single(void) +{ + int fd, fdh, i, rc; + + init_test2(&fd); + init_helper(&fdh); + + for (i = 0; i < 11; i++) { + + prepare_helper(fdh, &tc[i].test_params, &tc[i].test_params.pid); + usleep(400 * MSECS); + + trace_process(fd, tc[i].test_params.pid); + usleep(400 * MSECS); + + start_helper(fdh, tc[i].test_params.idx); + usleep(400 * MSECS); + + /* check proc for schedule stats */ + tracer_proc_read_values(&ts[0], 1); + rc = tracer_proc_check_values(&ts[0], &tc[i], 1); + + memset(&ts[0], 0, sizeof(struct tracer_stats)); + + untrace_process(fd, tc[i].test_params.pid); + usleep(400 * MSECS); + stop_helper(fdh, tc[i].test_params.idx); + + usleep(400 * MSECS); + test(tc[i].test_name, rc == 1, tc[i].score); + } + + cleanup_helper(fdh); + cleanup_test2(fd); +} + + +static void test_multiple_zero_stats(void) +{ + int fd, fdh, i, rc; + struct test_case mz[16]; + struct tracer_stats zstats[16]; /* zstats, mz, wtf? */ + + for (i = 0; i < 16; i++) { + memset(&mz[i], 0, sizeof(struct test_case)); + snprintf(mz[i].test_params.thread_name, 16, "xthread-%d", i); + mz[i].test_params.idx = i; + } + + init_test2(&fd); + init_helper(&fdh); + + for (i = 0; i < 16; i++) { + prepare_helper(fdh, &mz[i].test_params, &mz[i].test_params.pid); + + trace_process(fd, mz[i].test_params.pid); + } + + usleep(400 * MSECS); + for (i = 0; i < 16; i++) + start_helper(fdh, mz[i].test_params.idx); + + usleep(400 * MSECS); + + /* check proc for schedule stats */ + + tracer_proc_read_values(&zstats[0], 16); + rc = tracer_proc_check_values(&zstats[0], &mz[0], 16); + + for (i = 0; i < 16; i++) + untrace_process(fd, mz[i].test_params.pid); + + for (i = 0; i < 16; i++) + stop_helper(fdh, mz[i].test_params.idx); + usleep(400 * MSECS); + + test("test_multiple_zero_stats", rc == 1, 5); + + cleanup_helper(fdh); + cleanup_test2(fd); +} + +/* + * FIXME: duplicate code + */ +static void test_multiple_nonzero_stats(void) +{ + int fd, fdh, i, rc; + struct test_case mz[16]; + struct tracer_stats zstats[16]; /* zstats, mz, wtf? */ + + for (i = 0; i < 16; i++) { + memset(&mz[i], 0, sizeof(struct test_case)); + snprintf(mz[i].test_params.thread_name, 16, "xthread-%d", i); + mz[i].test_params.up = i; + mz[i].test_params.down = i; + mz[i].test_params.sched = i; + mz[i].test_params.lock = i; + mz[i].test_params.unlock = i; + mz[i].test_params.idx = i; + } + + init_test2(&fd); + init_helper(&fdh); + + for (i = 0; i < 16; i++) { + prepare_helper(fdh, &mz[i].test_params, &mz[i].test_params.pid); + + trace_process(fd, mz[i].test_params.pid); + } + + usleep(400 * MSECS); + for (i = 0; i < 16; i++) + start_helper(fdh, mz[i].test_params.idx); + + usleep(400 * MSECS); + + /* check proc for schedule stats */ + + tracer_proc_read_values(&zstats[0], 16); + rc = tracer_proc_check_values(&zstats[0], &mz[0], 16); + + for (i = 0; i < 16; i++) + untrace_process(fd, mz[i].test_params.pid); + + for (i = 0; i < 16; i++) + stop_helper(fdh, mz[i].test_params.idx); + usleep(400 * MSECS); + + test("test_multiple_nonzero_stats", rc == 1, 12); + + cleanup_helper(fdh); + cleanup_test2(fd); +} + +/* + * FIXME: duplicate code + */ +static void test_decent_alloc_free(void) +{ + int fd, fdh, i, rc, j; + struct test_case mz[32]; + struct tracer_stats zstats[32]; /* zstats, mz, wtf? */ + + for (i = 0; i < 32; i++) { + memset(&mz[i], 0, sizeof(struct test_case)); + snprintf(mz[i].test_params.thread_name, 16, "xthread-%d", i); + mz[i].test_params.kcalls = 32; + for (j = 0; j < 32; j++) { + mz[i].test_params.alloc[j] = 8 * j * (i+1); + mz[i].test_params.free[j] = 1; + } + + mz[i].test_params.idx = i; + } + + init_test2(&fd); + init_helper(&fdh); + + for (i = 0; i < 32; i++) { + prepare_helper(fdh, &mz[i].test_params, &mz[i].test_params.pid); + + trace_process(fd, mz[i].test_params.pid); + } + + usleep(800 * MSECS); + for (i = 0; i < 32; i++) + start_helper(fdh, mz[i].test_params.idx); + + usleep(800 * MSECS); + + /* check proc for schedule stats */ + + tracer_proc_read_values(&zstats[0], 32); + rc = tracer_proc_check_values(&zstats[0], &mz[0], 32); + + for (i = 0; i < 32; i++) + untrace_process(fd, mz[i].test_params.pid); + + for (i = 0; i < 32; i++) + stop_helper(fdh, mz[i].test_params.idx); + usleep(800 * MSECS); + + test("test_decent_alloc_free", rc == 1, 12); + + cleanup_helper(fdh); + cleanup_test2(fd); +} + +/* + * FIXME: duplicate code + */ +static void test_mini_stress(void) +{ + int fd, fdh, i, rc; + struct test_case mz[32]; + struct tracer_stats zstats[32]; /* zstats, mz, wtf? */ + + for (i = 0; i < 32; i++) { + memset(&mz[i], 0, sizeof(struct test_case)); + snprintf(mz[i].test_params.thread_name, 16, "xthread-%d", i); + mz[i].test_params.up = 512 + i; + mz[i].test_params.down = 512 + i; + mz[i].test_params.sched = i; + mz[i].test_params.lock = 128 + i; + mz[i].test_params.unlock = 128 + i; + mz[i].test_params.idx = i; + } + + init_test2(&fd); + init_helper(&fdh); + + for (i = 0; i < 32; i++) { + prepare_helper(fdh, &mz[i].test_params, &mz[i].test_params.pid); + + trace_process(fd, mz[i].test_params.pid); + } + + usleep(800 * MSECS); + for (i = 0; i < 32; i++) + start_helper(fdh, mz[i].test_params.idx); + + usleep(800 * MSECS); + + /* check proc for schedule stats */ + + tracer_proc_read_values(&zstats[0], 32); + rc = tracer_proc_check_values(&zstats[0], &mz[0], 32); + + for (i = 0; i < 32; i++) + untrace_process(fd, mz[i].test_params.pid); + + for (i = 0; i < 32; i++) + stop_helper(fdh, mz[i].test_params.idx); + usleep(800 * MSECS); + + test("test_mini_stress", rc == 1, 12); + + cleanup_helper(fdh); + cleanup_test2(fd); +} + + + + +static void (*test_fun_array[])(void) = { + NULL, + test_insmod_rmmod, + test_open_dev_tracer, + test_dev_minor_major, + test_proc_entry_exists_after_insmod, + test_proc_entry_inexistent_after_rmmod, + test_single, + test_multiple_zero_stats, + test_multiple_nonzero_stats, + test_decent_alloc_free, + test_mini_stress, +}; + +/* + * Usage message for invalid executable call. + */ + +static void usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s test_no\n\n", argv0); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + int test_idx; + + if (argc != 2) + usage(argv[0]); + + test_idx = atoi(argv[1]); + + if (test_idx < 1 || + test_idx >= ARRAY_SIZE(test_fun_array)) { + fprintf(stderr, "Error: test index %d is out of bounds\n", + test_idx); + exit(EXIT_FAILURE); + } + + srand(time(NULL)); + srand48(time(NULL)); + test_fun_array[test_idx](); + + return 0; +} diff --git a/checker/1-tracer-checker/_test/tracer_test.h b/checker/1-tracer-checker/_test/tracer_test.h new file mode 100644 index 0000000000000000000000000000000000000000..ac189824743ee089092eb2bd6b0f3a565a218e7e --- /dev/null +++ b/checker/1-tracer-checker/_test/tracer_test.h @@ -0,0 +1,26 @@ +/* + * SO2 Kprobe based tracer - test suite specific header + * + * Authors: + * Daniel Baluta <daniel.baluta@gmail.com> + */ + +#ifndef TRACER_TEST_H_ +#define TRACER_TEST_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* tracer test suite macros and structures */ +#define MODULE_NAME "tracer" +#define MODULE_FILENAME MODULE_NAME ".ko" + +#define HELPER_MODULE_NAME "tracer_helper" +#define HELPER_MODULE_FILENAME HELPER_MODULE_NAME ".ko" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/1-tracer-checker/_test/util.h b/checker/1-tracer-checker/_test/util.h new file mode 100644 index 0000000000000000000000000000000000000000..72eb85e8200563a51dc0a86e5afcf5b311df9f8f --- /dev/null +++ b/checker/1-tracer-checker/_test/util.h @@ -0,0 +1,71 @@ +/* + * useful structures/macros + * + * Operating Systems 2 + */ + +#ifndef UTIL_H_ +#define UTIL_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) + +#include <windows.h> + +static VOID PrintLastError(const PCHAR message) +{ + CHAR errBuff[1024]; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + GetLastError(), + 0, + errBuff, + sizeof(errBuff) - 1, + NULL); + + fprintf(stderr, "%s: %s\n", message, errBuff); +} + +#define ERR(call_description) \ + do { \ + fprintf(stderr, "(%s, %d): ", \ + __FILE__, __LINE__); \ + PrintLastError(call_description); \ + } while (0) + +#elif defined(__linux__) + +/* error printing macro */ +#define ERR(call_description) \ + do { \ + fprintf(stderr, "(%s, %d): ", \ + __FILE__, __LINE__); \ + perror(call_description); \ + } while (0) + +#else + #error "Unknown platform" +#endif + +/* print error (call ERR) and exit */ +#define DIE(assertion, call_description) \ + do { \ + if (assertion) { \ + ERR(call_description); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/checker.sh b/checker/checker.sh index bf7f5dc1ee7d9521b0094e1fa6b3debccc04e283..47e5d87670c53f77547bd199ee977e9a6a008cc6 100755 --- a/checker/checker.sh +++ b/checker/checker.sh @@ -2,12 +2,21 @@ TIMEOUT=300 # 5 min SO2_WORKSPACE=/linux/tools/labs + ASSIGNMENT0_MOD=list.ko ASSIGNMENT0_DIR=${SO2_WORKSPACE}/skels/assignments/0-list ASSIGNMENT0_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/0-list-checker ASSIGNMENT0_OUTPUT=${SO2_WORKSPACE}/skels/0-list-output ASSIGNMENT0_FINISHED=${SO2_WORKSPACE}/skels/0-list-finished +ASSIGNMENT1_MOD=tracer.ko +ASSIGNMENT1_DIR=${SO2_WORKSPACE}/skels/assignments/1-tracer +ASSIGNMENT1_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/1-tracer-checker +ASSIGNMENT1_OUTPUT=${SO2_WORKSPACE}/skels/1-tracer-output +ASSIGNMENT1_FINISHED=${SO2_WORKSPACE}/skels/1-tracer-finished +ASSIGNMENT1_HEADER_OVERWRITE=${SO2_WORKSPACE}/templates/assignments/1-tracer/tracer.h +ASSIGNMENT1_CHECKER_AUX_LIST="${ASSIGNMENT1_CHECKER_DIR}/_helper/tracer_helper.ko" + usage() { echo "Usage: $0 <assignment>" @@ -50,7 +59,9 @@ run_checker() local output=$4 local finished=$5 local assignment=$6 - + local header_overwrite=$7 + local aux_modules=$8 + local module_path="${assignment_dir}/${assignment_mod}" echo "Copying the contents of src/ into $assignment_dir" @@ -61,8 +72,24 @@ run_checker() echo "$assignment_mod shouldn't exists. Removing ${module_path}" rm $module_path fi + + pushd $assignment_dir &> /dev/null + echo "Cleaning $assignment_dir => Will remove: *.o *.mod *.mod.c .*.cmd *.ko modules.order" + rm *.o &> /dev/null + rm *.mod &> /dev/null + rm *.mod.c &> /dev/null + rm .*.cmd &> /dev/null + rm *.ko &> /dev/null + rm modules.order &> /dev/null + + if [[ $header_overwrite != "" ]]; then + echo "Overwrite from $header_overwrite" + cp $header_overwrite . + fi + popd &> /dev/null + - pushd /linux/tools/labs &> /dev/null + pushd $SO2_WORKSPACE &> /dev/null if [ -f $output ]; then echo "Removing $output" rm $output &> /dev/null @@ -83,6 +110,15 @@ run_checker() # copy *.ko in checker echo "Copying $module_path into $checker_dir" cp $module_path $checker_dir + + # copy aux modules in checker + if [[ $aux_modules != "" ]]; then + for mod in $aux_modules + do + echo "Copying $mod in $checker_dir" + cp $mod $checker_dir + done + fi LINUX_ADD_CMDLINE="so2=$assignment" ./qemu/run-qemu.sh &> /dev/null & @@ -110,6 +146,11 @@ run_checker() case $1 in 0-list) run_checker $ASSIGNMENT0_MOD $ASSIGNMENT0_DIR $ASSIGNMENT0_CHECKER_DIR $ASSIGNMENT0_OUTPUT $ASSIGNMENT0_FINISHED $1 + ;; + 1-tracer) + run_checker $ASSIGNMENT1_MOD $ASSIGNMENT1_DIR $ASSIGNMENT1_CHECKER_DIR $ASSIGNMENT1_OUTPUT $ASSIGNMENT1_FINISHED $1 $ASSIGNMENT1_HEADER_OVERWRITE $ASSIGNMENT1_CHECKER_AUX_LIST + + ;; *) usage diff --git a/checker/checker_daemons/so2_vm_checker_daemon.sh b/checker/checker_daemons/so2_vm_checker_daemon.sh index 162bba828b1df65015196bfd5a3acf2b96d8015e..afc38b65f3a33a39cbb20e707d6dd1cd1b3790c1 100644 --- a/checker/checker_daemons/so2_vm_checker_daemon.sh +++ b/checker/checker_daemons/so2_vm_checker_daemon.sh @@ -6,6 +6,10 @@ ASSIGNMENT0_CHECKER=/home/root/skels/assignments/0-list-checker ASSIGNMENT0_OUTPUT=/home/root/skels/0-list-output ASSIGNMENT0_FINISHED=/home/root/skels/0-list-finished +ASSIGNMENT1_CHECKER=/home/root/skels/assignments/1-tracer-checker +ASSIGNMENT1_OUTPUT=/home/root/skels/1-tracer-output +ASSIGNMENT1_FINISHED=/home/root/skels/1-tracer-finished + assign0_list() { cd $ASSIGNMENT0_CHECKER @@ -14,6 +18,15 @@ assign0_list() cd - } +assign1_tracer() +{ + cd $ASSIGNMENT1_CHECKER + sh _checker &> $ASSIGNMENT1_OUTPUT + echo FINISHED &> $ASSIGNMENT1_FINISHED + cd - +} + + start() { local arg=$(cat /proc/cmdline | grep -o 'so2=[^ ]*' | cut -d= -f2) @@ -21,6 +34,9 @@ start() 0-list) assign0_list ;; + 1-tracer) + assign1_tracer + ;; *) echo "Unknown option" exit 0 diff --git a/local.sh b/local.sh index 4dbca4cd41908bb0f19f4439969230dc99653e73..6c0cc4986334fd091ed306122458c9a2123ae44a 100755 --- a/local.sh +++ b/local.sh @@ -10,16 +10,16 @@ cd "$(dirname "$0")" || exit 1 RED='\033[0;31m' NC='\033[0m' -DEFAULT_IMAGE_NAME="so2/0-list" +DEFAULT_IMAGE_NAME="so2/1-tracer" DEFAULT_TAG='latest' DEFAULT_REGISTRY='gitlab.cs.pub.ro:5050' SRC_DIR="$(realpath ./src)" CHECKER_BASE_DIR="$(realpath ./checker)" -ASSIGNMENT_CHECKER_DIR="$(realpath ./checker/0-list-checker)" +ASSIGNMENT_CHECKER_DIR="$(realpath ./checker/1-tracer-checker)" SO2_WORKSPACE=/linux/tools/labs -ASSIGNMENT_MOUNT_DIR="${SO2_WORKSPACE}/skels/assignments/0-list" -ASSIGNMENT_CHECKER_MOUNT_DIR="${SO2_WORKSPACE}/skels/assignments/0-list-checker" +ASSIGNMENT_MOUNT_DIR="${SO2_WORKSPACE}/skels/assignments/1-tracer" +ASSIGNMENT_CHECKER_MOUNT_DIR="${SO2_WORKSPACE}/skels/assignments/1-tracer-checker" MOUNT_PROJECT_DIRECTORY="/build/$USER/$(basename "$(pwd)")" #============================================================================= diff --git a/src/Kbuild b/src/Kbuild deleted file mode 100644 index 5e45a816841549a017843600ee193089cabdb5c1..0000000000000000000000000000000000000000 --- a/src/Kbuild +++ /dev/null @@ -1 +0,0 @@ -obj-m = list.o diff --git a/src/list.c b/src/list.c deleted file mode 100644 index 9584f21d3229ce9031bf6788a3f29a7b1898dc9d..0000000000000000000000000000000000000000 --- a/src/list.c +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -/* - * list.c - Linux kernel list API - * - * TODO 1/0: Fill in name / email - * Author: FirstName LastName <user@email.com> - */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> - -#define PROCFS_MAX_SIZE 512 - -#define procfs_dir_name "list" -#define procfs_file_read "preview" -#define procfs_file_write "management" - -struct proc_dir_entry *proc_list; -struct proc_dir_entry *proc_list_read; -struct proc_dir_entry *proc_list_write; - -/* TODO 2: define your list! */ - -static int list_proc_show(struct seq_file *m, void *v) -{ - /* TODO 3: print your list. One element / line. */ - seq_puts(m, "Remove this line\n"); - - return 0; -} - -static int list_read_open(struct inode *inode, struct file *file) -{ - return single_open(file, list_proc_show, NULL); -} - -static int list_write_open(struct inode *inode, struct file *file) -{ - return single_open(file, list_proc_show, NULL); -} - -static ssize_t list_write(struct file *file, const char __user *buffer, - size_t count, loff_t *offs) -{ - char local_buffer[PROCFS_MAX_SIZE]; - unsigned long local_buffer_size = 0; - - local_buffer_size = count; - if (local_buffer_size > PROCFS_MAX_SIZE) - local_buffer_size = PROCFS_MAX_SIZE; - - memset(local_buffer, 0, PROCFS_MAX_SIZE); - if (copy_from_user(local_buffer, buffer, local_buffer_size)) - return -EFAULT; - - /* local_buffer contains your command written in /proc/list/management - * TODO 4/0: parse the command and add/delete elements. - */ - - return local_buffer_size; -} - -static const struct proc_ops r_pops = { - .proc_open = list_read_open, - .proc_read = seq_read, - .proc_release = single_release, -}; - -static const struct proc_ops w_pops = { - .proc_open = list_write_open, - .proc_write = list_write, - .proc_release = single_release, -}; - -static int list_init(void) -{ - proc_list = proc_mkdir(procfs_dir_name, NULL); - if (!proc_list) - return -ENOMEM; - - proc_list_read = proc_create(procfs_file_read, 0000, proc_list, - &r_pops); - if (!proc_list_read) - goto proc_list_cleanup; - - proc_list_write = proc_create(procfs_file_write, 0000, proc_list, - &w_pops); - if (!proc_list_write) - goto proc_list_read_cleanup; - - return 0; - -proc_list_read_cleanup: - proc_remove(proc_list_read); -proc_list_cleanup: - proc_remove(proc_list); - return -ENOMEM; -} - -static void list_exit(void) -{ - proc_remove(proc_list); -} - -module_init(list_init); -module_exit(list_exit); - -MODULE_DESCRIPTION("Linux kernel list API"); -/* TODO 5: Fill in your name / email address */ -MODULE_AUTHOR("FirstName LastName <your@email.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/src/tracer.h b/src/tracer.h new file mode 100644 index 0000000000000000000000000000000000000000..7a55257b770aaf29bfbc2f44186dd466a48f74f7 --- /dev/null +++ b/src/tracer.h @@ -0,0 +1,21 @@ +/* + * SO2 kprobe based tracer header file + * + * this is shared with user space + */ + +#ifndef TRACER_H__ +#define TRACER_H__ 1 + +#include <asm/ioctl.h> +#ifndef __KERNEL__ +#include <sys/types.h> +#endif /* __KERNEL__ */ + +#define TRACER_DEV_MINOR 42 +#define TRACER_DEV_NAME "tracer" + +#define TRACER_ADD_PROCESS _IOW(_IOC_WRITE, 42, pid_t) +#define TRACER_REMOVE_PROCESS _IOW(_IOC_WRITE, 43, pid_t) + +#endif /* TRACER_H_ */