summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ogness <john.ogness@linutronix.de>2017-12-19 10:59:40 +0100
committerJohn Ogness <john.ogness@linutronix.de>2017-12-19 10:59:40 +0100
commit270520b4a2eac8725c8575c3180964289722e191 (patch)
treed9512cd96d0c52e14293c3f8bc19fd168ee14025
parent27209bb802048f4803d9cd9a5c2f99d613986446 (diff)
schulung_tools: add various demos and tools
Different tools have been used by various trainers as demos. Put all these into master so they are available to all trainers. ipc_pipe: ipc demo using pipes ipc_shm: ipc demo using shared memory libduma: source and instructions for compiling libduma matrix: demo of good and bad cache access mtrace: patch and infos for using mtrace with ASLR rtex: demo of handling page faults Signed-off-by: John Ogness <john.ogness@linutronix.de>
-rw-r--r--schulung_tools/ipc_pipe/Makefile12
-rw-r--r--schulung_tools/ipc_pipe/README10
-rw-r--r--schulung_tools/ipc_pipe/main.c115
-rw-r--r--schulung_tools/ipc_shm/Makefile25
-rw-r--r--schulung_tools/ipc_shm/README30
-rw-r--r--schulung_tools/ipc_shm/recv.c49
-rw-r--r--schulung_tools/ipc_shm/send.c41
-rw-r--r--schulung_tools/ipc_shm/shm.c73
-rw-r--r--schulung_tools/ipc_shm/shm.h17
-rw-r--r--schulung_tools/libduma/0003-fix-C++14.patch65
-rw-r--r--schulung_tools/libduma/README10
-rw-r--r--schulung_tools/libduma/duma_2.5.15-1.1.diff.gzbin0 -> 7729 bytes
-rw-r--r--schulung_tools/libduma/duma_2.5.15.orig.tar.gzbin0 -> 240479 bytes
-rw-r--r--schulung_tools/matrix/Makefile12
-rw-r--r--schulung_tools/matrix/README7
-rw-r--r--schulung_tools/matrix/main.c91
-rw-r--r--schulung_tools/mtrace/Makefile7
-rw-r--r--schulung_tools/mtrace/README19
-rw-r--r--schulung_tools/mtrace/leak.c15
-rw-r--r--schulung_tools/mtrace/mtrace.patch25
-rw-r--r--schulung_tools/rtex/Makefile7
-rw-r--r--schulung_tools/rtex/README.txt46
-rw-r--r--schulung_tools/rtex/func.c69
-rw-r--r--schulung_tools/rtex/main.c60
-rw-r--r--schulung_tools/rtex/rt.c72
25 files changed, 877 insertions, 0 deletions
diff --git a/schulung_tools/ipc_pipe/Makefile b/schulung_tools/ipc_pipe/Makefile
new file mode 100644
index 0000000..a5c8404
--- /dev/null
+++ b/schulung_tools/ipc_pipe/Makefile
@@ -0,0 +1,12 @@
+CFLAGS=-O0 -g -Wall -pedantic -lpthread -std=gnu99
+LDFLAGS=-lpthread
+
+all: main
+
+main.o: main.c
+main: main.o
+
+clean:
+ rm -rf main *.o
+
+.PHONY: clean
diff --git a/schulung_tools/ipc_pipe/README b/schulung_tools/ipc_pipe/README
new file mode 100644
index 0000000..c0fccac
--- /dev/null
+++ b/schulung_tools/ipc_pipe/README
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# run test program
+sudo trace-cmd record \
+ -e sched:sched_switch \
+ -e sched:sched_wakeup \
+ taskset 1 chrt -f 80 ./main -s 1000000
+
+# view results
+kernelshark
diff --git a/schulung_tools/ipc_pipe/main.c b/schulung_tools/ipc_pipe/main.c
new file mode 100644
index 0000000..bda2da7
--- /dev/null
+++ b/schulung_tools/ipc_pipe/main.c
@@ -0,0 +1,115 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+
+/* pipe file descriptors */
+static int fd[2];
+
+/* send/write size */
+static size_t size;
+
+/* options */
+static struct option long_opts[] = {
+ { "size", required_argument, NULL, 's' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void *reader(void *arg)
+{
+ unsigned char buf[size];
+ ssize_t read_ = 0;
+
+ while (read_ < size) {
+ ssize_t ret;
+
+ ret = read(fd[0], buf + read_, size - read_);
+ if (ret < 0) {
+ perror("read() failed");
+ exit(EXIT_FAILURE);
+ }
+ read_ += ret;
+ }
+
+ printf("Reader: got data\n");
+ return NULL;
+}
+
+static void *writer(void *arg)
+{
+ unsigned char buf[size];
+ ssize_t written = 0;
+
+ sleep(1);
+
+ while (written < size) {
+ ssize_t ret;
+
+ ret = write(fd[1], buf + written, size - written);
+ if (ret < 0) {
+ perror("write() failed");
+ exit(EXIT_FAILURE);
+ }
+
+ written += ret;
+ }
+
+ printf("Writer: sent data\n");
+
+ sleep(1);
+
+ return NULL;
+}
+
+static inline void print_usage_and_die(void)
+{
+ fprintf(stderr, "usage: pipedemo [-s <size>]\n");
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t tid1, tid2;
+ char *end;
+ int c;
+
+ size = 4 << 10;
+ while ((c = getopt_long(argc, argv, "s:", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 's':
+ size = strtoull(optarg, &end, 10);
+ if (end == optarg || *end != '\0' || errno == ERANGE) {
+ fprintf(stderr, "given size is not valid\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ default:
+ print_usage_and_die();
+ }
+ }
+
+ if (pipe(fd) < 0) {
+ perror("pipe() failed");
+ return EXIT_FAILURE;
+ }
+
+ if (pthread_create(&tid1, NULL, reader, NULL) ||
+ pthread_create(&tid2, NULL, writer, NULL)) {
+ fprintf(stderr, "pthread_create() failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (pthread_setname_np(tid1, "pipedemo-reader") ||
+ pthread_setname_np(tid2, "pipedemo-writer")) {
+ fprintf(stderr, "pthread_create() failed\n");
+ return EXIT_FAILURE;
+ }
+
+ pthread_join(tid1, NULL);
+ pthread_join(tid2, NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/schulung_tools/ipc_shm/Makefile b/schulung_tools/ipc_shm/Makefile
new file mode 100644
index 0000000..0c6ca9e
--- /dev/null
+++ b/schulung_tools/ipc_shm/Makefile
@@ -0,0 +1,25 @@
+CC = $(CROSS_COMPILE)gcc
+CFLAGS = -Wall -Werror -g -O0 -std=c99 -D_GNU_SOURCE
+LDFLAGS = -lpthread -lrt shm.c
+CFLAGS += \
+ -Wchar-subscripts \
+ -Wmissing-declarations \
+ -Wmissing-prototypes \
+ -Wnested-externs \
+ -Wpointer-arith \
+ -Wcast-align \
+ -Wfloat-equal \
+ -Wsign-compare
+
+all: recv send
+
+recv: recv.c shm.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o$@ $<
+
+send: send.c shm.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o$@ $<
+
+clean:
+ rm -f send recv
+
+.PHONY: clean all
diff --git a/schulung_tools/ipc_shm/README b/schulung_tools/ipc_shm/README
new file mode 100644
index 0000000..bea6c98
--- /dev/null
+++ b/schulung_tools/ipc_shm/README
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# build programs send/recv
+make
+
+# find uprobe offsets (search for "uprobe")
+objdump -D -F -S send | less
+objdump -D -F -S recv | less
+
+# create uprobe events (as root)
+echo "p:sending `pwd`/send:0x1055" > /sys/kernel/debug/tracing/uprobe_events
+echo "p:received `pwd`/recv:0xff1" >> /sys/kernel/debug/tracing/uprobe_events
+
+# run receiver
+sudo taskset 1 chrt -f 80 ./recv &
+
+# run sender (wrapping with trace-cmd)
+sudo trace-cmd record \
+ -e sched:sched_switch \
+ -e sched:sched_wakeup \
+ -e sched:sched_pi_setprio \
+ -e uprobes:sending \
+ -e uprobes:received \
+ -e raw_syscalls:sys_enter \
+ -e raw_syscalls:sys_exit \
+ taskset 1 chrt -f 70 ./send
+(type message and hit return)
+
+# view results
+kernelshark
diff --git a/schulung_tools/ipc_shm/recv.c b/schulung_tools/ipc_shm/recv.c
new file mode 100644
index 0000000..8a08338
--- /dev/null
+++ b/schulung_tools/ipc_shm/recv.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "shm.h"
+
+int main(void)
+{
+ struct share_data *data;
+ int rv;
+
+ if (create_share() != 0)
+ return 1;
+
+ data = get_share();
+ if (!data)
+ return 1;
+
+ init_share(data);
+
+ if (pthread_mutex_lock(&data->m) != 0)
+ return 1;
+
+ printf("recv (%d): waiting for signal\n", getpid());
+
+ do {
+ rv = pthread_cond_wait(&data->c, &data->m);
+ if (rv != 0) {
+ if (rv == EOWNERDEAD) {
+ printf("recv(%d): recover mutex\n", getpid());
+ data->msg[0] = 0;
+ pthread_mutex_consistent(&data->m);
+ } else {
+ /* maybe rv == ENOTRECOVERABLE */
+ return 1;
+ }
+ }
+ } while (rv != 0);
+
+ /* set "received" uprobe here */
+
+ printf("recv (%d): received signal: %s\n", getpid(), data->msg);
+ pthread_mutex_unlock(&data->m);
+
+ remove_share();
+
+ return 0;
+}
diff --git a/schulung_tools/ipc_shm/send.c b/schulung_tools/ipc_shm/send.c
new file mode 100644
index 0000000..028610f
--- /dev/null
+++ b/schulung_tools/ipc_shm/send.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "shm.h"
+
+int main(void)
+{
+ struct share_data *data;
+ int rv;
+
+ data = get_share();
+ if (!data)
+ return 1;
+
+ rv = pthread_mutex_lock(&data->m);
+ if (rv != 0) {
+ if (rv == EOWNERDEAD) {
+ printf("send (%d): recover mutex\n", getpid());
+ data->msg[0] = 0;
+ pthread_mutex_consistent(&data->m);
+ } else {
+ /* maybe rv == ENOTRECOVERABLE */
+ return 1;
+ }
+ }
+
+ printf("send (%d): type message and hit RETURN to send signal\n",
+ getpid());
+ fgets(data->msg, sizeof(data->msg), stdin);
+
+ /* set "sending" uprobe here */
+
+ pthread_cond_signal(&data->c);
+ pthread_mutex_unlock(&data->m);
+
+ sleep(1);
+
+ return 0;
+}
diff --git a/schulung_tools/ipc_shm/shm.c b/schulung_tools/ipc_shm/shm.c
new file mode 100644
index 0000000..777fe79
--- /dev/null
+++ b/schulung_tools/ipc_shm/shm.c
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include "shm.h"
+
+#define SHARE_NAME "/ipc.shm"
+
+/* open and return a pointer to shared data */
+struct share_data *get_share(void)
+{
+ struct share_data *data;
+ int fd;
+
+ fd = shm_open(SHARE_NAME, O_RDWR, 0);
+ if (fd == -1)
+ return NULL;
+
+ data = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+
+ return data;
+}
+
+/* initialize shared data */
+void init_share(struct share_data *data)
+{
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+ pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
+ pthread_mutex_init(&data->m, &mattr);
+
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&data->c, &cattr);
+}
+
+/* create a zero'd out file of size 1 page */
+int create_share(void)
+{
+ int ret;
+ int fd;
+
+ fd = shm_open(SHARE_NAME, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
+ if (fd == -1)
+ return -1;
+
+ ret = ftruncate(fd, sysconf(_SC_PAGESIZE));
+
+ close(fd);
+
+ return ret;
+}
+
+/* remove the share */
+void remove_share(void)
+{
+ shm_unlink(SHARE_NAME);
+}
diff --git a/schulung_tools/ipc_shm/shm.h b/schulung_tools/ipc_shm/shm.h
new file mode 100644
index 0000000..35acd0a
--- /dev/null
+++ b/schulung_tools/ipc_shm/shm.h
@@ -0,0 +1,17 @@
+#ifndef _SHM_H
+#define _SHM_H
+
+#include <pthread.h>
+
+struct share_data {
+ pthread_mutex_t m;
+ pthread_cond_t c;
+ char msg[2048];
+};
+
+extern int create_share(void);
+extern void init_share(struct share_data *data);
+extern struct share_data *get_share(void);
+extern void remove_share(void);
+
+#endif /* _SHM_H */
diff --git a/schulung_tools/libduma/0003-fix-C++14.patch b/schulung_tools/libduma/0003-fix-C++14.patch
new file mode 100644
index 0000000..d19213c
--- /dev/null
+++ b/schulung_tools/libduma/0003-fix-C++14.patch
@@ -0,0 +1,65 @@
+dumapp: fix for C++14
+
+With C++14, the way exceptions are specified has changed (somehow, don't
+ask me), thus causing build failures:
+
+ dumapp.cpp: In function ‘void* operator new(std::size_t)’:
+ dumapp.cpp:192:19: error: declaration of ‘void* operator new(std::size_t) throw (std::bad_alloc)’ has a different exception specifier
+ void * DUMA_CDECL operator new( DUMA_SIZE_T size )
+ ^~~~~~~~
+ In file included from dumapp.cpp:39:0:
+ dumapp.h:91:23: note: from previous declaration ‘void* operator new(std::size_t)’
+ void * DUMA_CDECL operator new(DUMA_SIZE_T) throw(std::bad_alloc);
+ ^~~~~~~~
+
+This is most evident with gcc-6.x, since the default C++ standard has
+changed from C++11 to C++14, thus exposing these new failures.
+
+Fix that by guarding the exception handling, a bit like was done
+with GRASS GIS (thanks DuckDuckGo):
+
+ https://trac.osgeo.org/grass/changeset?old_path=%2F&old=68817&new_path=%2F&new=68818&sfp_email=&sfph_mail=
+
+Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
+
+---
+Note: The last commit in DUMA's CVS repo was more than 7 years ago.
+I doubt it is still active, so the patch was not sent upstream. :-/
+
+diff -durN duma-2.5.15.orig/dumapp.cpp duma-2.5.15/dumapp.cpp
+--- duma-2.5.15.orig/dumapp.cpp 2008-08-03 22:46:06.000000000 +0200
++++ duma-2.5.15/dumapp.cpp 2016-07-10 21:55:22.670386099 +0200
+@@ -190,7 +190,9 @@
+ * (11) = (a) ; ASW
+ */
+ void * DUMA_CDECL operator new( DUMA_SIZE_T size )
++#ifdef DUMA_EXCEPTION_SPECS
+ throw(std::bad_alloc)
++#endif
+ {
+ return duma_new_operator(size, EFA_NEW_ELEM, true DUMA_PARAMS_UK);
+ }
+@@ -254,7 +256,9 @@
+ * (21) = (a) ; AAW
+ */
+ void * DUMA_CDECL operator new[]( DUMA_SIZE_T size )
++#ifdef DUMA_EXCEPTION_SPECS
+ throw(std::bad_alloc)
++#endif
+ {
+ return duma_new_operator(size, EFA_NEW_ARRAY, true DUMA_PARAMS_UK);
+ }
+diff -durN duma-2.5.15.orig/dumapp.h duma-2.5.15/dumapp.h
+--- duma-2.5.15.orig/dumapp.h 2009-04-11 14:41:44.000000000 +0200
++++ duma-2.5.15/dumapp.h 2016-07-10 21:55:22.670386099 +0200
+@@ -35,6 +35,10 @@
+
+ #include "duma.h"
+
++#if __cplusplus < 201103L
++ #define DUMA_EXCEPTION_SPECS 1
++#endif
++
+ /* remove previous macro definitions */
+ #include "noduma.h"
+
diff --git a/schulung_tools/libduma/README b/schulung_tools/libduma/README
new file mode 100644
index 0000000..41e1109
--- /dev/null
+++ b/schulung_tools/libduma/README
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+tar xzf duma_2.5.15.orig.tar.gz
+cd duma_2_5_15
+zcat ../duma_2.5.15-1.1.diff.gz | patch -p1
+cat ../0003-fix-C++14.patch | patch -p1
+sed -i -e 's/-Wl,-soname,$(DUMASO)/-Wl,-soname,$(DUMASO_LINK1)/' GNUmakefile
+make
+sudo cp libduma.so.0.0.0 /usr/lib/x86_64-linux-gnu
+sudo ldconfig
diff --git a/schulung_tools/libduma/duma_2.5.15-1.1.diff.gz b/schulung_tools/libduma/duma_2.5.15-1.1.diff.gz
new file mode 100644
index 0000000..037872a
--- /dev/null
+++ b/schulung_tools/libduma/duma_2.5.15-1.1.diff.gz
Binary files differ
diff --git a/schulung_tools/libduma/duma_2.5.15.orig.tar.gz b/schulung_tools/libduma/duma_2.5.15.orig.tar.gz
new file mode 100644
index 0000000..504ef58
--- /dev/null
+++ b/schulung_tools/libduma/duma_2.5.15.orig.tar.gz
Binary files differ
diff --git a/schulung_tools/matrix/Makefile b/schulung_tools/matrix/Makefile
new file mode 100644
index 0000000..a5c8404
--- /dev/null
+++ b/schulung_tools/matrix/Makefile
@@ -0,0 +1,12 @@
+CFLAGS=-O0 -g -Wall -pedantic -lpthread -std=gnu99
+LDFLAGS=-lpthread
+
+all: main
+
+main.o: main.c
+main: main.o
+
+clean:
+ rm -rf main *.o
+
+.PHONY: clean
diff --git a/schulung_tools/matrix/README b/schulung_tools/matrix/README
new file mode 100644
index 0000000..40a1e75
--- /dev/null
+++ b/schulung_tools/matrix/README
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# investigate good cache usage
+sudo perf stat ./main -1
+
+# investigate bad cache usage
+sudo perf stat ./main -2
diff --git a/schulung_tools/matrix/main.c b/schulung_tools/matrix/main.c
new file mode 100644
index 0000000..9a6f2fa
--- /dev/null
+++ b/schulung_tools/matrix/main.c
@@ -0,0 +1,91 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+
+/* matrix dimension */
+static size_t dim;
+
+/* options */
+static struct option long_opts[] = {
+ { "dim", required_argument, NULL, 'd' },
+ { "1", no_argument, NULL, '1' },
+ { "2", no_argument, NULL, '2' },
+ { NULL, 0, NULL, 0 },
+};
+
+static int visit_every_element_1(const int *matrix, size_t dim)
+{
+ int sum = 0;
+
+ /* row by row */
+ for (int i = 0; i < dim; ++i) {
+ for (int j = 0; j < dim; ++j) {
+ sum += matrix[i * dim + j];
+ }
+ }
+
+ return sum;
+}
+
+static int visit_every_element_2(const int *matrix, size_t dim)
+{
+ int sum = 0;
+
+ /* column by column */
+ for (int j = 0; j < dim; ++j) {
+ for (int i = 0; i < dim; ++i) {
+ sum += matrix[i * dim + j];
+ }
+ }
+
+ return sum;
+}
+
+static inline void print_usage_and_die(void)
+{
+ fprintf(stderr, "usage: matrix [-1|-2] [-d <size>]\n");
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+ int *matrix, sum;
+ char *end;
+ int c, choice = 1;
+
+ dim = 10000;
+ while ((c = getopt_long(argc, argv, "d:12", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ dim = strtoull(optarg, &end, 10);
+ if (end == optarg || *end != '\0' || errno == ERANGE) {
+ fprintf(stderr, "given dim is not valid\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case '1':
+ choice = 1;
+ break;
+ case '2':
+ choice = 2;
+ break;
+ default:
+ print_usage_and_die();
+ }
+ }
+
+ matrix = (int *)calloc(dim * dim, sizeof(int));
+ if (!matrix) {
+ perror("malloc() failed");
+ return EXIT_FAILURE;
+ }
+
+ sum = choice == 1 ? visit_every_element_1(matrix, dim) :
+ visit_every_element_2(matrix, dim);
+ printf("Bogus result: %d\n", sum);
+
+ return EXIT_SUCCESS;
+}
diff --git a/schulung_tools/mtrace/Makefile b/schulung_tools/mtrace/Makefile
new file mode 100644
index 0000000..224a9fa
--- /dev/null
+++ b/schulung_tools/mtrace/Makefile
@@ -0,0 +1,7 @@
+leak: leak.c
+ gcc -g -O0 -o$@ $<
+
+clean:
+ rm -f leak
+
+.PHONY: clean
diff --git a/schulung_tools/mtrace/README b/schulung_tools/mtrace/README
new file mode 100644
index 0000000..0c61b6d
--- /dev/null
+++ b/schulung_tools/mtrace/README
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# copy original mtrace (perl script)
+cp /usr/bin/mtrace .
+
+# patch it to support new ASLR semantics
+patch -p1 < mtrace.patch
+
+# disable randomization
+setarch `uname -m` -R /bin/bash
+
+# build leaking test program
+make
+
+# run test program and record leaks
+MALLOC_TRACE=./leak.trace ./leak
+
+# view found
+./mtrace ./leak ./leak.trace
diff --git a/schulung_tools/mtrace/leak.c b/schulung_tools/mtrace/leak.c
new file mode 100644
index 0000000..45ec7b1
--- /dev/null
+++ b/schulung_tools/mtrace/leak.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <mcheck.h>
+
+void a(void)
+{
+ malloc(1024);
+}
+
+int main(void)
+{
+ mtrace();
+ a();
+ return 0;
+}
diff --git a/schulung_tools/mtrace/mtrace.patch b/schulung_tools/mtrace/mtrace.patch
new file mode 100644
index 0000000..874388c
--- /dev/null
+++ b/schulung_tools/mtrace/mtrace.patch
@@ -0,0 +1,25 @@
+Patch mtrace to deal with new ASLR issues.
+--- a/mtrace 2017-06-15 20:17:14.000000000 +0100
++++ b/mtrace 2017-12-13 12:09:31.584089486 +0000
+@@ -75,10 +75,10 @@ if ($#ARGV == 0) {
+ } else {
+ $prog = "./$binary";
+ }
+- if (open (LOCS, "env LD_TRACE_LOADED_OBJECTS=1 $prog |")) {
++ if (open (LOCS, "env LD_TRACE_PRELINKING=1 $prog |")) {
+ while (<LOCS>) {
+ chop;
+- if (/^.*=> (.*) .(0x[0123456789abcdef]*).$/) {
++ if (/^.*=> (.*) .(0x[0123456789abcdef]*),.*/) {
+ $locs{$1} = $2;
+ }
+ }
+@@ -111,7 +111,7 @@ sub location {
+ my $searchaddr;
+ return $cache{$addr} if (exists $cache{$addr});
+ if ($locs{$prog} ne "") {
+- $searchaddr = sprintf "%#x", $addr - $locs{$prog};
++ $searchaddr = sprintf "%#x", hex($addr) - hex($locs{$prog});
+ } else {
+ $searchaddr = $addr;
+ $prog = $binary;
diff --git a/schulung_tools/rtex/Makefile b/schulung_tools/rtex/Makefile
new file mode 100644
index 0000000..330bc01
--- /dev/null
+++ b/schulung_tools/rtex/Makefile
@@ -0,0 +1,7 @@
+rtex: main.c func.c rt.c
+ gcc -O0 $+ -o$@
+
+clean:
+ rm -f rtex
+
+.PHONY: clean
diff --git a/schulung_tools/rtex/README.txt b/schulung_tools/rtex/README.txt
new file mode 100644
index 0000000..ce33bcf
--- /dev/null
+++ b/schulung_tools/rtex/README.txt
@@ -0,0 +1,46 @@
+The rtex program demonstrates the importance of using several tricks to
+avoid page faults and memory allocation during critical runtime. It displays
+the amount of time taken and generated page faults when performing various
+tasks. This clearly shows the expense due to page faults.
+
+Running the program without any (or invalid) arguments shows the usage
+help. The program argument specifies which rt tweak should be performed
+and if the runtime tests should be performed.
+
+The tests can be performed on any Linux system (embedded, desktop, server,
+with or without PREEMPT_RT). The results will be the same.
+
+The following is a summary of the most important results:
+
+running the test without any rt tweaks
+ - main setup very fast
+ - first big malloc+access very expensive
+ - second big malloc+access very expensive
+ - first big stack very expensive
+$ sudo ./rtex 0x10
+options: 0x10
+ 5715 ns 0 faults : main setup
+ 17466150 ns 2561 faults : testfunc_malloc (0)
+ 14963662 ns 2560 faults : testfunc_malloc (1)
+ 2517772 ns 0 faults : testfunc_malloc (2)
+ 2577208 ns 0 faults : testfunc_malloc (3)
+ 6113558 ns 1847 faults : testfunc_deepstack (0)
+ 390386 ns 0 faults : testfunc_deepstack (1)
+ 321146 ns 0 faults : testfunc_deepstack (2)
+ 317585 ns 0 faults : testfunc_deepstack (3)
+
+running the test with all rt tweaks
+ - main setup very slow (does it really matter?)
+ - no expensive malloc+access calls
+ - no expensive big stacks
+$ sudo ./rtex 0x1f
+options: 0x1f
+ 31276098 ns 7083 faults : main setup
+ 2657466 ns 0 faults : testfunc_malloc (0)
+ 2519973 ns 0 faults : testfunc_malloc (1)
+ 2543506 ns 0 faults : testfunc_malloc (2)
+ 2750682 ns 0 faults : testfunc_malloc (3)
+ 526604 ns 0 faults : testfunc_deepstack (0)
+ 324543 ns 0 faults : testfunc_deepstack (1)
+ 318580 ns 0 faults : testfunc_deepstack (2)
+ 317265 ns 0 faults : testfunc_deepstack (3)
diff --git a/schulung_tools/rtex/func.c b/schulung_tools/rtex/func.c
new file mode 100644
index 0000000..4b88166
--- /dev/null
+++ b/schulung_tools/rtex/func.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+void timestamp(const char *msg, int val)
+{
+ static struct timespec tsl;
+ static long minfltl;
+
+ struct timespec ts;
+ unsigned long diff;
+ struct rusage r;
+ long rdiff;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ getrusage(RUSAGE_SELF, &r);
+
+ if (tsl.tv_sec != 0) {
+ diff = (ts.tv_sec - tsl.tv_sec) * 1000000000;
+ diff += ts.tv_nsec - tsl.tv_nsec;
+
+ rdiff = r.ru_minflt - minfltl;
+
+ printf("% 10d ns % 5ld faults : %s", diff, rdiff, msg);
+ if (val >= 0)
+ printf(" (%d)", val);
+ printf("\n");
+ }
+
+ tsl = ts;
+ minfltl = r.ru_minflt;
+}
+
+/* 10 MiB */
+#define SIZE (10 * 1024 * 1024)
+
+void testfunc_malloc(void)
+{
+ char *p;
+
+ p = malloc(SIZE);
+ if (!p) {
+ fprintf(stderr, "MALLOC FAILED: %s:%d\n", __FILE__, __LINE__);
+ return;
+ }
+
+ memset(p, 42, SIZE);
+
+ if (p[SIZE - 1] != 42)
+ fprintf(stderr, "MEMSET FAILED: %s:%d\n", __FILE__, __LINE__);
+
+ free(p);
+}
+
+void recursive_stack(int i)
+{
+ char buf[1024];
+ if (i > 0)
+ recursive_stack(i - 1);
+}
+
+void testfunc_deepstack(void)
+{
+ char buf[1024];
+ recursive_stack(7 * 1024);
+}
diff --git a/schulung_tools/rtex/main.c b/schulung_tools/rtex/main.c
new file mode 100644
index 0000000..a68baf4
--- /dev/null
+++ b/schulung_tools/rtex/main.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+extern void timestamp(const char *msg, int val);
+extern void testfunc_malloc(void);
+extern void testfunc_deepstack(void);
+extern void setup_rt(unsigned int opts);
+
+static void usage(const char *cmd)
+{
+ printf("usage: %s [opts-bitmask]\n", cmd);
+ printf(" opts-bits:\n");
+ printf(" 0x01 = mallopt\n");
+ printf(" 0x02 = mlockall\n");
+ printf(" 0x04 = prefault-stack\n");
+ printf(" 0x08 = prefault-heap\n");
+ printf(" 0x10 = run tests\n");
+ printf("\n");
+ printf(" 0x10 = no rt tweaks + tests\n");
+ printf(" 0x1f = full rt tweaks + tests\n");
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+
+ if (argc != 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ if (sscanf(argv[1], "%x", &i) != 1) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ printf("options: 0x%x\n", i);
+
+ timestamp("init", -1);
+
+ setup_rt(i);
+
+ timestamp("main setup", -1);
+
+ if (i & 0x10) {
+ for (i = 0; i < 4; i++) {
+ testfunc_malloc();
+ timestamp("testfunc_malloc", i);
+ }
+
+ for (i = 0; i < 4; i++) {
+ testfunc_deepstack();
+ timestamp("testfunc_deepstack", i);
+ }
+ }
+
+ return 0;
+}
diff --git a/schulung_tools/rtex/rt.c b/schulung_tools/rtex/rt.c
new file mode 100644
index 0000000..86a6506
--- /dev/null
+++ b/schulung_tools/rtex/rt.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <sys/mman.h>
+
+extern void timestamp(const char *msg, int val);
+extern void testfunc_malloc(void);
+extern void testfunc_deepstack(void);
+
+void prefault_stack(void)
+{
+#define STACK_SIZE (7680 * 1024) /* 7.5 MiB */
+ char buf[STACK_SIZE];
+ long pagesize;
+ int i;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+
+ for (i = 0; i < STACK_SIZE; i += pagesize)
+ buf[i] = 0;
+}
+
+void prefault_heap(void)
+{
+#define HEAP_SIZE (20 * 1024 * 1024) /* 20 MiB */
+ long pagesize;
+ char *buf;
+ int i;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+
+ buf = malloc(HEAP_SIZE);
+ if (!buf) {
+ fprintf(stderr, "MALLOC FAILED: %s:%d\n", __FILE__, __LINE__);
+ return;
+ }
+
+ for (i = 0; i < HEAP_SIZE; i += pagesize)
+ buf[i] = 0;
+
+ free(buf);
+}
+
+void setup_rt(unsigned int opts)
+{
+ if (opts & 0x1) {
+ if (mallopt(M_TRIM_THRESHOLD, -1) == 0) {
+ fprintf(stderr, "MALLOPT FAILED: %s:%d\n",
+ __FILE__, __LINE__);
+ }
+
+ if (mallopt(M_MMAP_MAX, 0) == 0) {
+ fprintf(stderr, "MALLOPT FAILED: %s:%d\n",
+ __FILE__, __LINE__);
+ }
+ }
+
+ if (opts & 0x2) {
+ if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
+ fprintf(stderr, "MLOCKALL FAILED: %s:%d\n",
+ __FILE__, __LINE__);
+ }
+ }
+
+ if (opts & 0x4)
+ prefault_stack();
+
+ if (opts & 0x8)
+ prefault_heap();
+}
+