summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Bütow <pbuetow@mimecast.com>2018-03-30 12:38:57 +0100
committerPaul Bütow <pbuetow@mimecast.com>2018-03-30 12:38:57 +0100
commit0bf0f8cc86cab23668e45fbca3a4f46aa87cfe88 (patch)
tree6002540e7902278dc04eeb9950fd1f0685b1b377
parent49612db2713ccce2022495814c724e290c0e2b0f (diff)
parent79b99c0ad4c1761b4da71721d18c94648fb64d32 (diff)
Merge branch 'develop'0.4
-rw-r--r--Makefile2
-rw-r--r--README.md4
-rw-r--r--ioriot/Makefile4
-rw-r--r--ioriot/src/capture/capture.c2
-rw-r--r--ioriot/src/datas/btree.c125
-rw-r--r--ioriot/src/datas/btree.h10
-rw-r--r--ioriot/src/defaults.h10
-rw-r--r--ioriot/src/generate/generate.c49
-rw-r--r--ioriot/src/generate/gioop.c19
-rw-r--r--ioriot/src/generate/gioop.h1
-rw-r--r--ioriot/src/generate/vsize.c77
-rw-r--r--ioriot/src/generate/vsize.h12
-rw-r--r--ioriot/src/init/init.c30
-rw-r--r--ioriot/src/init/itask.c6
-rw-r--r--ioriot/src/init/itask.h3
-rw-r--r--ioriot/src/init/ithread.c10
-rw-r--r--ioriot/src/main.c6
-rw-r--r--ioriot/src/meta/meta.c2
-rw-r--r--ioriot/src/opcodes.h1
-rw-r--r--ioriot/src/options.c2
-rw-r--r--ioriot/src/replay/replay.c2
-rw-r--r--ioriot/src/replay/rioop.c1
-rw-r--r--ioriot/src/replay/rstats.c2
-rw-r--r--ioriot/src/utests.c4
-rw-r--r--ioriot/src/utils/futils.c23
-rw-r--r--ioriot/src/utils/futils.h9
-rw-r--r--ioriot/src/utils/utils.c34
-rw-r--r--systemtap/src/ioriot.stp28
-rw-r--r--systemtap/src/javaioriot.stp28
-rw-r--r--systemtap/src/targetedioriot.stp28
30 files changed, 376 insertions, 158 deletions
diff --git a/Makefile b/Makefile
index d5ab493..7e7099d 100644
--- a/Makefile
+++ b/Makefile
@@ -17,3 +17,5 @@ loc:
wc -l ./systemtap/src/*.stp ./ioriot/src/*.{h,c} ./ioriot/src/*/*.{h,c} | tail -n 1
doxygen:
doxygen ./docs/doxygen.conf
+test:
+ $(MAKE) -C ioriot test
diff --git a/README.md b/README.md
index be6aa8f..e668f94 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,7 @@ sudo ioriot -c ~/io.capture -m targetedioriot.ko -p PID
The resulting capture log looks like this and can be multiple GB in size:
```sh
+#|capture_version=2|
t=1511381122062;:,i=7764:8093;:,o=open;:,d=162;:,p=///usr/local/mimecast/someapp/somesubdir/vd11-9:1;:,f=0;:,m=438;:,
t=1511381122062;:,i=7764:8093;:,o=fstat;:,d=162;:,s=0;:,
t=1511381122062;:,i=7764:8093;:,o=read;:,d=162;:,b=12;:,
@@ -186,7 +187,7 @@ After producing ``io.capture`` it must be pre-processed. The resulting replay lo
To generate the the replay log ``io.replay`` from the capture log ``io.capture`` run:
```sh
-sudo ioriot -c io.capture -r io.replay -n NAME -u USER
+ioriot -c io.capture -r io.replay -n NAME -u USER
```
In which NAME is a freely chosen name and USER must be a valid system user. It is the system user under which the replay test will run. This command also creates all required top level directories such as ``/.ioriot/NAME/``, ``/mnt/.ioriot/NAME/`` in all mounted file systems. These are the directories where the replay test will read/write files from/to. These directories will belong to user USER.
@@ -339,6 +340,7 @@ Currently, these file I/O related syscalls are supported (as of CentOS 7):
open
openat
lseek
+llseek
fcntl
creat
write
diff --git a/ioriot/Makefile b/ioriot/Makefile
index d65915c..7474d05 100644
--- a/ioriot/Makefile
+++ b/ioriot/Makefile
@@ -11,7 +11,7 @@ all: compile
quick: clean ctags compile sudo_install
cshell: compile
gdb -ex='break main; run' --args ./$(NAME)
-test: compile
+gdbtest: compile
gdb -ex=run --args ./$(NAME) -U
compile: $(OBJS)
$(CC) $(STATIC) $(DEBUG) $(LIBS) $(OBJS) -o $(NAME)
@@ -34,3 +34,5 @@ todo:
fgrep ../TODO ./src/*
ctags:
ctags ./src/*.{h,c} ./src/*/*.{h,c}
+test: clean all
+ ./ioriot -U
diff --git a/ioriot/src/capture/capture.c b/ioriot/src/capture/capture.c
index aaad6cb..5e8771d 100644
--- a/ioriot/src/capture/capture.c
+++ b/ioriot/src/capture/capture.c
@@ -77,7 +77,7 @@ status_e capture_run(options_s *opts)
Out("NOTICE: It is good practise first to stop all processes, then to ");
Out("start capturing, and then to start all processes again. The reason ");
Out("is that processes may have already open file handles. In that case ");
- Out("I/O Riot would be unable to replay these! This may be improved ");
+ Out("I/O Riot would be unable to replay these! This may be improved ");
Put("in a future release!");
Put("To abort capturing now send Ctrl+C, otherwise wait 1h");
Put("Capturing I/O via: '%s'", staprun_command);
diff --git a/ioriot/src/datas/btree.c b/ioriot/src/datas/btree.c
index 90cb889..21b5ab9 100644
--- a/ioriot/src/datas/btree.c
+++ b/ioriot/src/datas/btree.c
@@ -62,6 +62,14 @@ void* btree_get(btree_s* b, long key)
return btreelem_get_r(b->root, key);
}
+bool btree_has_range_l(btree_s* b, const long start, const long end)
+{
+ if (b->root == NULL)
+ return false;
+
+ return btreelem_has_range_lr(b->root, start, end);
+}
+
long btree_get_l(btree_s* b, long key)
{
void *data = btree_get(b, key);
@@ -71,12 +79,12 @@ long btree_get_l(btree_s* b, long key)
return -1;
}
-void btree_ensure_range_l(btree_s* b, long from, long to)
+void btree_ensure_range_l(btree_s* b, const long start, const long end, const long threshold)
{
if (b->root == NULL) {
- btree_insert(b, from, (void*)to);
+ btree_insert(b, start, (void*)end);
} else {
- if (1 == btreelem_ensure_range_lr(b->root, from, to))
+ if (1 == btreelem_ensure_range_lr(b->root, start, end, threshold))
b->size++;
}
}
@@ -87,9 +95,9 @@ void btree_print(btree_s* b)
btreelem_print_r(b->root, 1);
}
-void btree_run_cb2(btree_s* b, void (*cb)(void *data, void *data2))
+void btree_run_cb2(btree_s* b, void (*cb)(long key, void *data, void *data2), void *data2)
{
- btreelem_run_cb2_r(b->root, cb);
+ btreelem_run_cb2_r(b->root, cb, data2);
}
btreelem_s* btreelem_new(long key, void *data)
@@ -107,7 +115,7 @@ void btreelem_destroy_r(btreelem_s* e)
{
if (e->left)
btreelem_destroy_r(e->left);
- if (e->right)
+ if (e->right)
btreelem_destroy_r(e->right);
free(e);
@@ -150,39 +158,46 @@ int btreelem_insert_r(btreelem_s* e, long key, void *data)
return ret;
}
-int btreelem_ensure_range_lr(btreelem_s *e, const long from, const long to)
+int btreelem_ensure_range_lr(btreelem_s *e, const long start, const long end, const long threshold)
{
int ret = 0;
long value = (long) e->data;
- if (e->key == from) {
- if (value < to) {
- e->data = (void*) to;
+ //Debug("%ld %ld %ld", start, end, threshold);
+
+ if (e->key == start) {
+ if (value < end) {
+ e->data = (void*) end;
} else {
// Nothing to do, range already present
}
- } else if (e->key > from) {
+ } else if (e->key > start) {
if (e->left == NULL) {
- e->left = btreelem_new(from, (void*)to);
- ret = 1;
+ if (value <= end) {
+ e->key = start;
+ e->data = (void*)end;
+ } else {
+ e->left = btreelem_new(start, (void*)end);
+ ret = 1;
+ }
} else {
- ret = btreelem_ensure_range_lr(e->left, from, to);
+ ret = btreelem_ensure_range_lr(e->left, start, end, threshold);
}
- } else { // if (e->key < from)
- if (value >= from) {
- if (value < to) {
- e->data = (void*) to;
+ } else { // if (e->key < start)
+ if (value >= start) {
+ if (value < end) {
+ e->data = (void*) end;
} else {
// Nothing to do, range already present
}
} else {
if (e->right == NULL) {
- e->right = btreelem_new(from, (void*)to);
+ e->right = btreelem_new(start, (void*)end);
ret = 1;
} else {
- ret = btreelem_ensure_range_lr(e->right, from, to);
+ ret = btreelem_ensure_range_lr(e->right, start, end, threshold);
}
}
}
@@ -209,6 +224,27 @@ void* btreelem_get_r(btreelem_s* e, long key)
return data;
}
+bool btreelem_has_range_lr(btreelem_s* e, const long start, const long end)
+{
+ long value = (long)e->data;
+
+ if (e->key <= start && value >= end) {
+ return true;
+
+ } else if (e->key > start) {
+ if (e->left)
+ return btreelem_has_range_lr(e->left, start, end);
+ else
+ return false;
+
+ } else {
+ if (e->right)
+ return btreelem_has_range_lr(e->right, start, end);
+ else
+ return false;
+ }
+}
+
void btreelem_print_r(btreelem_s* e, int depth)
{
if (!e)
@@ -225,18 +261,18 @@ void btreelem_print_r(btreelem_s* e, int depth)
btreelem_print_r(e->right, depth+1);
}
-void btreelem_run_cb2_r(btreelem_s* e, void (*cb)(void *data, void *data2))
+void btreelem_run_cb2_r(btreelem_s* e, void (*cb)(long key, void *data, void *data2), void *data2)
{
if (!e)
return;
- cb((void*)(long)e->key, e->data);
+ cb(e->key, e->data, data2);
if (e->left)
- btreelem_run_cb2_r(e->left, cb);
+ btreelem_run_cb2_r(e->left, cb, data2);
if (e->right)
- btreelem_run_cb2_r(e->right, cb);
+ btreelem_run_cb2_r(e->right, cb, data2);
}
void btree_test(void)
@@ -250,7 +286,7 @@ void btree_test(void)
assert(2 == b->size);
assert(1 == btree_insert(b, 3, (void*)3));
assert(3 == b->size);
- assert(1 == (long)btree_get(b, 1));
+ assert(1 == btree_get_l(b, 1));
assert(1 == btree_insert(b, 1234, somedata));
assert(4 == b->size);
@@ -266,7 +302,7 @@ void btree_test(void)
assert(0 == btree_insert(b, 666, somedata));
assert(1 == btree_insert(b, 42, (void*)42));
- assert(42 == (long)btree_get(b, 42));
+ assert(42 == btree_get_l(b, 42));
btree_print(b);
btree_destroy(b);
@@ -274,29 +310,40 @@ void btree_test(void)
b = btree_new();
assert(0 == b->size);
- btree_ensure_range_l(b, 0, 23);
+ btree_ensure_range_l(b, 0, 23, 0);
assert(btree_get_l(b, 0) == (long) btree_get(b, 0));
assert(23 == btree_get_l(b, 0));
+ assert(btree_has_range_l(b, 2, 10));
- btree_ensure_range_l(b, 0, 23);
- btree_ensure_range_l(b, 10, 10);
- btree_ensure_range_l(b, 22, 25);
- assert(25 == btree_get_l(b, 0));
- assert(1 == b->size);
+ assert(!btree_has_range_l(b, 300, 325));
+ btree_ensure_range_l(b, 300, 325, 0);
+ assert(2 == b->size);
+ assert(325 == btree_get_l(b, 300));
+ assert(btree_has_range_l(b, 300, 325));
+ assert(!btree_has_range_l(b, 300, 326));
- btree_ensure_range_l(b, 300, 25);
+ btree_ensure_range_l(b, 200, 3321, 0);
assert(2 == b->size);
- btree_ensure_range_l(b, 200, 25);
- assert(3 == b->size);
- assert(25 == btree_get_l(b, 200));
+ btree_ensure_range_l(b, 200, 1000, 0);
+ assert(2 == b->size);
+ assert(3321 == btree_get_l(b, 200));
- btree_ensure_range_l(b, 200, 1000);
- assert(3 == b->size);
- assert(1000 == btree_get_l(b, 200));
+ btree_ensure_range_l(b, 0, 23, 0);
+ btree_ensure_range_l(b, 10, 10, 0);
+ btree_ensure_range_l(b, 22, 25, 0);
+ assert(25 == btree_get_l(b, 0));
+ assert(2 == b->size);
+ assert(!btree_has_range_l(b, 4000, 4000));
+ btree_ensure_range_l(b, 4000, 4000, 0);
+ assert(3 == b->size);
+ assert(btree_has_range_l(b, 4000, 4000));
+ assert(!btree_has_range_l(b, 4000, 4001));
+ assert(!btree_has_range_l(b, 3999, 4000));
btree_print(b);
+ btree_print(b);
btree_destroy(b);
exit(0);
diff --git a/ioriot/src/datas/btree.h b/ioriot/src/datas/btree.h
index a1ca7ef..0a05cee 100644
--- a/ioriot/src/datas/btree.h
+++ b/ioriot/src/datas/btree.h
@@ -41,9 +41,10 @@ void btree_destroy2(btree_s *b);
int btree_insert(btree_s *b, long key, void *data);
void* btree_get(btree_s *b, long key);
long btree_get_l(btree_s *b, long key);
-void btree_ensure_range_l(btree_s *b, const long from, const long to);
+bool btree_has_range_l(btree_s *b, const long start, const long end);
+void btree_ensure_range_l(btree_s *b, const long start, const long end, const long threshold);
void btree_print(btree_s *b);
-void btree_run_cb2(btree_s* b, void (*cb)(void *data, void *data2));
+void btree_run_cb2(btree_s* b, void (*cb)(long key, void *data, void *data2), void *data2);
void btree_test(void);
btreelem_s* btreelem_new(long key, void *data);
@@ -51,8 +52,9 @@ void btreelem_destroy_r(btreelem_s *e);
void btreelem_destroy_r2(btreelem_s *e);
int btreelem_insert_r(btreelem_s *e, long key, void *data);
void* btreelem_get_r(btreelem_s *e, long key);
-int btreelem_ensure_range_lr(btreelem_s *e, const long from, const long to);
+bool btreelem_has_range_lr(btreelem_s *e, const long start, const long end);
+int btreelem_ensure_range_lr(btreelem_s *e, const long start, const long end, const long threshold);
void btreelem_print_r(btreelem_s *e, int depth);
-void btreelem_run_cb2_r(btreelem_s* e, void (*cb)(void *data, void *data2));
+void btreelem_run_cb2_r(btreelem_s* e, void (*cb)(long key, void *data, void *data2), void *data2);
#endif // BTREE_H
diff --git a/ioriot/src/defaults.h b/ioriot/src/defaults.h
index c0833c8..b4adc83 100644
--- a/ioriot/src/defaults.h
+++ b/ioriot/src/defaults.h
@@ -18,9 +18,9 @@
#include "utils/utils.h"
/** Version of the supported .capture format */
-#define CAPTURE_VERSION 1
+#define CAPTURE_VERSION 2
/** Version of the supported .replay format */
-#define REPLAY_VERSION 1
+#define REPLAY_VERSION 2
/** Max amount of tokens per line in the .capture file */
#define MAX_TOKENS 10
/** Max line length in either .capture or .replay file */
@@ -28,13 +28,15 @@
/** Controls how many tasks can be queued and buffered per worker thread */
#define TASK_BUFFER_PER_THREAD 512
/** Version of I/O Riot */
-#define IORIOT_VERSION "0.4-develop"
+#define IORIOT_VERSION "0.4"
/** Copyright information */
-#define IORIOT_COPYRIGHT "Mimecast 2017, 2018 (c)"
+#define IORIOT_COPYRIGHT "(c) Mimecast 2018"
/** Max open files resource user limit */
#define SET_RLIMIT_NOFILE 369216
/** Max processes resource user limit */
#define SET_RLIMIT_NPROC 30768
+/** Ignore file hole size */
+#define IGNORE_FILE_HOLE_BYTES 1024*1024*10
// The following are for debugging purposes only
diff --git a/ioriot/src/generate/generate.c b/ioriot/src/generate/generate.c
index baff401..0185c50 100644
--- a/ioriot/src/generate/generate.c
+++ b/ioriot/src/generate/generate.c
@@ -68,7 +68,6 @@ void generate_destroy(generate_s *g)
free(g);
}
-
status_e generate_run(options_s *opts)
{
generate_s *g = generate_new(opts);
@@ -82,8 +81,23 @@ status_e generate_run(options_s *opts)
set_limits_drop_root(opts->user);
+ // Check for correct capture format version
+ meta_s *meta = meta_new(capture_fd);
+ meta_read_start(meta);
+
+ long capture_version = 0;
+ if (meta_read_l(meta, "capture_version", &capture_version)) {
+ Put("Capture version is '%ld'", capture_version);
+ if (capture_version != CAPTURE_VERSION) {
+ Error(".capture file of incompatible version, got %x, expected %x",
+ (int)capture_version, CAPTURE_VERSION);
+ }
+ }
+
+ meta_destroy(meta);
+
// Reserve first few bytes for meta information
- meta_s *meta = meta_new(g->replay_fd);
+ meta = meta_new(g->replay_fd);
meta_reserve(meta);
// The writer will write the .replay file
@@ -109,6 +123,9 @@ status_e generate_run(options_s *opts)
// either the parser or the writer thread!
while ((read = getline(&line, &len, capture_fd)) != -1) {
+ if (line[0] == '#')
+ continue;
+
if (0 > ++g->lineno) {
Error("lineno:%lu Line number overflow", g->lineno);
}
@@ -189,14 +206,30 @@ status_e generate_run(options_s *opts)
return SUCCESS;
}
-void generate_write_init_cb(void *data)
+void _write_ranges_cb(long start, void *data, void *data2)
{
- vsize_s *l = data;
- generate_s *g = l->generate;
+ vsize_s *v = data2;
+ generate_s *g = v->generate;
+ long end = (long) data;
+ long bytes = end-start;
+ if (bytes > 0) {
+ fprintf(g->replay_fd, "%d|%d|%ld|%ld|%s|\n",
+ v->is_dir, v->is_file, start, bytes, v->path);
+ }
+}
- if (l->required && strlen(l->path) > 0) {
- fprintf(g->replay_fd, "%d|%d|%ld|%s|\n",
- l->is_dir, l->is_file, 666L, l->path);
+void generate_write_init_cb(void *data)
+{
+ vsize_s *v = data;
+ generate_s *g = v->generate;
+
+ if (v->required && strlen(v->path) > 0) {
+ if (v->read_ranges) {
+ btree_run_cb2(v->read_ranges, _write_ranges_cb, data);
+ } else if (v->bytes >= 0) {
+ fprintf(g->replay_fd, "%d|%d|%ld|%ld|%s|\n",
+ v->is_dir, v->is_file, 0L, v->bytes, v->path);
+ }
}
}
diff --git a/ioriot/src/generate/gioop.c b/ioriot/src/generate/gioop.c
index df48ef8..d5b9a28 100644
--- a/ioriot/src/generate/gioop.c
+++ b/ioriot/src/generate/gioop.c
@@ -109,6 +109,9 @@ status_e gioop_run(gwriter_s *w, gtask_s *t)
} else if (Eq(t->op, "lseek")) {
Cleanup(gioop_lseek(w, t, g));
+ } else if (Eq(t->op, "llseek")) {
+ Cleanup(gioop_llseek(w, t, g));
+
} else if (Eq(t->op, "getdents")) {
Cleanup(gioop_getdents(w, t, g));
@@ -559,6 +562,22 @@ status_e gioop_lseek(gwriter_s *w, gtask_s *t, generate_s *g)
return SUCCESS;
}
+status_e gioop_llseek(gwriter_s *w, gtask_s *t, generate_s *g)
+{
+ if (!t->has_fd) {
+ return ERROR;
+ }
+
+ generate_vsize_by_path(g, t, t->vfd->path);
+ Gioop_write(LLSEEK, "%ld|%ld|%ld|%ld|llseek",
+ t->mapped_fd, t->offset, t->whence, t->bytes);
+
+ if (t->bytes >= 0)
+ vsize_seek(t->vsize, t->vfd, t->bytes);
+
+ return SUCCESS;
+}
+
status_e gioop_getdents(gwriter_s *w, gtask_s *t, generate_s *g)
{
if (!t->has_fd) {
diff --git a/ioriot/src/generate/gioop.h b/ioriot/src/generate/gioop.h
index ad49713..1df57c1 100644
--- a/ioriot/src/generate/gioop.h
+++ b/ioriot/src/generate/gioop.h
@@ -79,6 +79,7 @@ status_e gioop_readlinkat(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_write(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_writev(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_lseek(gwriter_s *w, gtask_s *t, generate_s *g);
+status_e gioop_llseek(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_getdents(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_mkdir(gwriter_s *w, gtask_s *t, generate_s *g);
status_e gioop_rmdir(gwriter_s *w, gtask_s *t, generate_s *g);
diff --git a/ioriot/src/generate/vsize.c b/ioriot/src/generate/vsize.c
index 93be77b..b38f1bc 100644
--- a/ioriot/src/generate/vsize.c
+++ b/ioriot/src/generate/vsize.c
@@ -17,7 +17,6 @@
#include "generate.h"
// Helper macros
-
#define _Set_file(v) v->is_file = true; v->unsure = v->is_dir = false
#define _Set_dir(v) v->is_dir = true; v->unsure = v->is_file = false
#define _Set_unsure(v) v->unsure = true
@@ -40,8 +39,9 @@ vsize_s* vsize_new(char *file_path, const unsigned long id,
v->required = false;
v->unsure = false;
v->updates = 0;
- v->vsize = 0;
- v->ranges = NULL;
+ v->bytes = 0;
+ v->read_ranges = NULL;
+ v->write_ranges = NULL;
return v;
}
@@ -51,8 +51,10 @@ void vsize_destroy(vsize_s *v)
if (!v)
return;
- if (v->ranges)
- btree_destroy(v->ranges);
+ if (v->read_ranges)
+ btree_destroy(v->read_ranges);
+ if (v->write_ranges)
+ btree_destroy(v->write_ranges);
free(v->path);
free(v);
@@ -170,52 +172,67 @@ void vsize_rename(vsize_s *v, vsize_s *v2,
}
}
-void vsize_ensure_data_range(vsize_s *v, const long from, const long to)
+void vsize_ensure_data_range(vsize_s *v, btree_s **ranges, const long offset, const long bytes)
{
- if (v->ranges == NULL) {
- if (from == 0) {
- if (v->vsize < to) {
+ if (*ranges == NULL) {
+ if (offset == 0) {
+ if (v->bytes + IGNORE_FILE_HOLE_BYTES < bytes) {
// No file hole, just set the new (larger) vsize
- v->vsize = to;
+ v->bytes = bytes;
} else {
// Nothing to do, vsize is already sufficient
}
- } else { // if (from > 0)
- if (from <= v->vsize) {
+ } else { // if (offset > 0)
+ if (offset <= v->bytes) {
// We won't add a hole to the file here either
- if (v->vsize < to) {
- // No file hole, just set the new (larger) vsize
- v->vsize = to;
+ if (v->bytes < bytes) {
+ // No file hole, just set the new (larger) bytes
+ v->bytes = bytes;
} else {
- // Nothing to do, vsize is already sufficient
+ // Nothing to do, bytes is already sufficient
}
- } else { // if (from > v->vsize)
- // From offset larger than vsize, we have a hole in the file!
- v->ranges = btree_new();
+ } else { // if (offset > v->bytes)
+ // From offset larger than bytes, we have a hole in the file!
+ *ranges = btree_new();
// Insert root data range
- btree_insert(v->ranges, 0, (void*)v->vsize);
+ btree_insert(*ranges, 0, (void*)v->bytes);
// This value is not used anymore, we use the btree instead from now
// to store all used data ranges of a file.
- v->vsize = -1;
+ v->bytes = -1;
// Rerun this function with a data range btree initialised
- vsize_ensure_data_range(v, from, to);
+ vsize_ensure_data_range(v, ranges, offset, bytes);
}
}
- } else { // if (v->ranges != NULL)
- btree_ensure_range_l(v->ranges, from, to);
+ } else { // if (*ranges != NULL)
+ btree_ensure_range_l(*ranges, offset, offset+bytes,
+ IGNORE_FILE_HOLE_BYTES);
}
}
void vsize_read(vsize_s *v, void *vfd, const char *path, const int bytes)
{
vfd_s *vfd_ = vfd;
- // We may try to read data from a file with holes!
- vsize_ensure_data_range(v, vfd_->offset, bytes);
+
+ if (v->write_ranges == NULL ||
+ !btree_has_range_l(v->write_ranges, vfd_->offset, vfd_->offset+bytes)) {
+ vsize_ensure_data_range(v, &v->read_ranges, vfd_->offset, bytes);
+ _Set_required(v);
+ _Set_file(v);
+ }
+
+ vfd_->offset += bytes;
+ v->updates++;
+}
+
+void vsize_write(vsize_s *v, void *vfd, const char *path, const int bytes)
+{
+ vfd_s *vfd_ = vfd;
+ vsize_ensure_data_range(v, &v->write_ranges, vfd_->offset, bytes);
vfd_->offset += bytes;
v->updates++;
}
@@ -227,14 +244,8 @@ void vsize_seek(vsize_s *v, void *vfd, const long new_offset)
// The file's offset can be greater than the file's current size, in which
// case the next write to the file will extend the file. This is referred
// to as creating a hole in a file.
- vfd_->offset = new_offset;
- v->updates++;
-}
-void vsize_write(vsize_s *v, void *vfd, const char *path, const int bytes)
-{
- vfd_s *vfd_ = vfd;
- vfd_->offset += bytes;
+ vfd_->offset = new_offset;
v->updates++;
}
diff --git a/ioriot/src/generate/vsize.h b/ioriot/src/generate/vsize.h
index d0111fa..ffda9e5 100644
--- a/ioriot/src/generate/vsize.h
+++ b/ioriot/src/generate/vsize.h
@@ -30,8 +30,9 @@
*/
typedef struct vsize_s_ {
char *path; /**< The path to the file/directory */
- long vsize; /**< The virtual size */
- btree_s *ranges; /**< Used to store used data ranges in a file with holes */
+ long bytes; /**< The virtual size */
+ btree_s *read_ranges; /**< Used to store used data ranges in a file with holes */
+ btree_s *write_ranges; /**< Used to store used data ranges in a file with holes */
unsigned long id; /**< The vsize id */
void *generate; /**< A pointer to the generate object */
bool renamed; /**< True if file/dir has been renamed */
@@ -78,10 +79,11 @@ void init_parent_dir(vsize_s *v, const char *path);
* we have holes in the file to be replayed.
*
* @param v The virtual size object
- * @param from The start offset
- * @param to The end offset
+ * @param ranges The ranges object to operaet on
+ * @param offset The start offset
+ * @param bytes The amount of bytes
*/
-void vsize_ensure_data_range(vsize_s *v, const long from, const long to);
+void vsize_ensure_data_range(vsize_s *v, btree_s **ranges, const long offset, const long bytes);
/**
* @brief Adjust vsize on open
diff --git a/ioriot/src/init/init.c b/ioriot/src/init/init.c
index e375379..659d59a 100644
--- a/ioriot/src/init/init.c
+++ b/ioriot/src/init/init.c
@@ -21,7 +21,6 @@
#include "../mounts.h"
#include "../utils/futils.h"
-
init_s *init_new(options_s *opts)
{
init_s *i = Malloc(init_s);
@@ -60,12 +59,12 @@ void init_extract_header(init_s *i, off_t *init_offset)
meta_s *m = meta_new(i->replay_fd);
meta_read_start(m);
- long version = 0;
- if (meta_read_l(m, "version", &version)) {
- Put("Replay version is '%ld'", version);
- if (version != REPLAY_VERSION) {
+ long replay_version = 0;
+ if (meta_read_l(m, "replay_version", &replay_version)) {
+ Put("Replay version is '%ld'", replay_version);
+ if (replay_version != REPLAY_VERSION) {
Error(".replay file of incompatible version, got %x, expected %x",
- (int)version, REPLAY_VERSION);
+ (int)replay_version, REPLAY_VERSION);
}
}
@@ -118,7 +117,8 @@ status_e init_run(options_s *opts)
fseeko(i->replay_fd, init_offset, SEEK_SET);
bool is_file = false, is_dir = false;
- long vsize = 0;
+ long offset = 0;
+ long bytes = 0;
char *path;
// Stats
@@ -136,6 +136,7 @@ status_e init_run(options_s *opts)
// Process the INIT section of the .replay file line by line.
while ((read = getline(&line, &len, i->replay_fd)) != -1) {
+ //Debug(line);
char *tok = strtok_r(line, "|", &saveptr);
for (int ntok = 0; tok; ntok++) {
@@ -147,12 +148,18 @@ status_e init_run(options_s *opts)
is_file = atoi(tok) == 1;
break;
case 2:
- vsize = atol(tok);
- if (vsize < 0) {
- Error("Size overflow");
+ offset = atol(tok);
+ if (offset < 0) {
+ Error("Offset overflow: '%ld'", offset);
}
break;
case 3:
+ bytes = atol(tok);
+ if (bytes < 0) {
+ Error("Size overflow: '%ld'", bytes);
+ }
+ break;
+ case 4:
path = tok;
break;
default:
@@ -178,7 +185,8 @@ status_e init_run(options_s *opts)
} else if (is_file) {
task->is_file = true;
- task->vsize = vsize;
+ task->bytes = bytes;
+ task->offset = offset;
}
task->path = Clone(path);
diff --git a/ioriot/src/init/itask.c b/ioriot/src/init/itask.c
index f04ce33..de7c551 100644
--- a/ioriot/src/init/itask.c
+++ b/ioriot/src/init/itask.c
@@ -35,7 +35,7 @@ void itask_destroy(itask_s *task)
void itask_reset_stats(itask_s *task)
{
task->is_dir = task->is_file = false;
- task->sizes_created = task->vsize = 0;
+ task->sizes_created = task->offset = task->bytes = 0;
task->dirs_created = task->files_created = 0;
if (task->path) {
@@ -60,7 +60,7 @@ void itask_extract_stats(itask_s *task, long* dirs_created, long *files_created,
void itask_print(itask_s *task)
{
- Put("itask(%p): is_dir:%d is_file:%d vsize:%ld path:%s",
+ Put("itask(%p): is_dir:%d is_file:%d offset:%ld bytes:%ld path:%s",
(void*)task, task->is_dir, task->is_file,
- task->vsize, task->path);
+ task->offset, task->bytes, task->path);
}
diff --git a/ioriot/src/init/itask.h b/ioriot/src/init/itask.h
index b10d515..21afba4 100644
--- a/ioriot/src/init/itask.h
+++ b/ioriot/src/init/itask.h
@@ -23,7 +23,8 @@
typedef struct itask_s_ {
bool is_dir;
bool is_file;
- long vsize;
+ long offset;
+ long bytes;
char *path;
long dirs_created;
long files_created;
diff --git a/ioriot/src/init/ithread.c b/ioriot/src/init/ithread.c
index a580e70..bcc31f3 100644
--- a/ioriot/src/init/ithread.c
+++ b/ioriot/src/init/ithread.c
@@ -90,9 +90,13 @@ void ithread_run_task(ithread_s *t, itask_s *task)
} else if (task->is_file) {
if (!ensure_file_exists(task->path, &task->dirs_created)) {
task->files_created++;
- if (task->vsize > 0) {
- append_random_to_file(task->path, task->vsize);
- task->sizes_created += task->vsize;
+ if (task->bytes > 0) {
+ if (task->offset > 0) {
+ write_random_to_file(task->path, task->bytes, task->offset);
+ } else {
+ append_random_to_file(task->path, task->bytes);
+ }
+ task->sizes_created += task->bytes;
}
}
}
diff --git a/ioriot/src/main.c b/ioriot/src/main.c
index c987fe3..d7c6204 100644
--- a/ioriot/src/main.c
+++ b/ioriot/src/main.c
@@ -74,6 +74,7 @@ static void _print_synopsis(void)
Put("\tioriot -d");
Put("\tioriot -P");
Put("\tioriot -T [-n NAME]");
+ Put("\tioriot -U");
Put("\tioriot -V");
}
@@ -99,6 +100,7 @@ static void _print_help(void)
Put("\t-R REPLAYFILE Init and replay in one run (-i and -r combined)");
Put("\t-S STATSFILE Write a stats file at the end of a test");
Put("\t-T Trash data directories");
+ Put("\t-U Run I/O Riot's unit tests");
Put("\t-P Purge all trash directories of all tests)");
Put("\t-V Print I/O Riot program version");
Put("\t-w WD_BASE The working directory's base path");
@@ -109,7 +111,7 @@ static void _print_help(void)
Put("\t 1.) sudo ioriot -c io.capture");
Put("\t 2.) sudo ioriot -r io.replay -c io.capture -u paul -n test1");
Put("\t 3.) sudo ioriot -i io.replay");
- Put("\t 4.) sudo ioriot -r io.replay -S");
+ Put("\t 4.) sudo ioriot -r io.replay -S stats.txt");
}
/**
@@ -232,7 +234,7 @@ int main(int argc, char **argv)
Cleanup(cleanup_run(opts));
} else if (opts->capture_file && !opts->replay_file) {
- // We are going to capture I/O
+ // We are going to capture I/O
Cleanup(capture_run(opts));
} else if (opts->capture_file && opts->replay_file) {
diff --git a/ioriot/src/meta/meta.c b/ioriot/src/meta/meta.c
index d56c17e..1902ac6 100644
--- a/ioriot/src/meta/meta.c
+++ b/ioriot/src/meta/meta.c
@@ -40,7 +40,7 @@ void meta_destroy(meta_s *m)
void meta_reserve(meta_s *m)
{
- // TODO: Use a hole in the .replay file to reserve space
+ // Improvemend: Use a hole in the .replay file to reserve space
char buf[_MAX_META_LEN];
Mset(&buf, '#', _MAX_META_LEN-1, char);
fprintf(m->replay_fd, "%s\n", buf);
diff --git a/ioriot/src/opcodes.h b/ioriot/src/opcodes.h
index 3d5c114..0dd060c 100644
--- a/ioriot/src/opcodes.h
+++ b/ioriot/src/opcodes.h
@@ -69,6 +69,7 @@ typedef enum {
FCNTL = 70,
GETDENTS,
LSEEK,
+ LLSEEK,
// mmap syscalls
MMAP2 = 80,
diff --git a/ioriot/src/options.c b/ioriot/src/options.c
index 01a5375..3808170 100644
--- a/ioriot/src/options.c
+++ b/ioriot/src/options.c
@@ -24,7 +24,7 @@ options_s *options_new()
o->wd_base = "/usr/local/ioriot";
o->num_workers = 4;
o->num_threads_per_worker = 128;
- o->user = "mcuser";
+ o->user = "nobody";
o->name = "test0";
o->init = false;
o->replay = false;
diff --git a/ioriot/src/replay/replay.c b/ioriot/src/replay/replay.c
index e4606d1..c7ea7e4 100644
--- a/ioriot/src/replay/replay.c
+++ b/ioriot/src/replay/replay.c
@@ -150,7 +150,7 @@ status_e replay_run(options_s *opts)
pid_t pid;
int rworker_status = SUCCESS;
- while ((pid = wait(&rworker_status)) > 0) {
+ while ((pid = wait(&rworker_status)) > 0) {
if (rworker_status != SUCCESS)
status = rworker_status;
diff --git a/ioriot/src/replay/rioop.c b/ioriot/src/replay/rioop.c
index 2e16c94..0ce19c5 100644
--- a/ioriot/src/replay/rioop.c
+++ b/ioriot/src/replay/rioop.c
@@ -151,6 +151,7 @@ void rioop_run(rprocess_s *p, rthread_s *t, rtask_s *task)
rioop_getdents(p, t, task);
break;
case LSEEK:
+ case LLSEEK:
rioop_lseek(p, t, task);
break;
diff --git a/ioriot/src/replay/rstats.c b/ioriot/src/replay/rstats.c
index c3e6e38..6def990 100644
--- a/ioriot/src/replay/rstats.c
+++ b/ioriot/src/replay/rstats.c
@@ -70,7 +70,7 @@ void rstats_stop(rstats_s* s)
{
gettimeofday(&s->end_time, NULL);
s->duration= ((s->end_time.tv_sec - s->start_time.tv_sec) * 1000
- + (s->end_time.tv_usec - s->start_time.tv_usec) / 1000) / 1000;
+ + (s->end_time.tv_usec - s->start_time.tv_usec) / 1000) / 1000;
}
diff --git a/ioriot/src/utests.c b/ioriot/src/utests.c
index 0fc53c3..7cae7e8 100644
--- a/ioriot/src/utests.c
+++ b/ioriot/src/utests.c
@@ -18,7 +18,6 @@
#include "datas/btree.h"
#include "datas/hmap.h"
#include "datas/list.h"
-#include "datas/ranges.h"
#include "datas/rbuffer.h"
#include "utils/utils.h"
@@ -27,9 +26,6 @@ void utests_run()
fprintf(stderr, "Running btree_test()\n");
btree_test();
- fprintf(stderr, "Running ranges_test()\n");
- ranges_test();
-
fprintf(stderr, "Running amap_test()\n");
amap_test();
diff --git a/ioriot/src/utils/futils.c b/ioriot/src/utils/futils.c
index 5b35618..366756a 100644
--- a/ioriot/src/utils/futils.c
+++ b/ioriot/src/utils/futils.c
@@ -21,11 +21,10 @@
#include "../macros.h"
-void append_random_to_file(char *path, unsigned long bytes)
+void _write_random_to_stream(FILE *fp, unsigned long bytes)
{
char *buf = NULL;
int max_chunk = 50000000; // 50 mebibyetes
- FILE *fp = Fopen(path, "a");
for (;;) {
if (bytes > max_chunk) {
@@ -54,7 +53,25 @@ void append_random_to_file(char *path, unsigned long bytes)
if (buf)
free(buf);
- fclose(fp);
+}
+
+void append_random_to_file(char *path, unsigned long bytes)
+{
+ FILE *fp = Fopen(path, "a");
+ if (fp) {
+ _write_random_to_stream(fp, bytes);
+ fclose(fp);
+ }
+}
+
+void write_random_to_file(char *path, unsigned long bytes, off_t offset)
+{
+ FILE *fp = Fopen(path, "w");
+ if (fp) {
+ fseek(fp, offset, SEEK_SET);
+ _write_random_to_stream(fp, bytes);
+ fclose(fp);
+ }
}
long ensure_dir_exists(const char *path)
diff --git a/ioriot/src/utils/futils.h b/ioriot/src/utils/futils.h
index 9afde1a..e8cff97 100644
--- a/ioriot/src/utils/futils.h
+++ b/ioriot/src/utils/futils.h
@@ -77,6 +77,15 @@ int mkdir_p(const char *path, mode_t mode, long *num_dirs_created);
void append_random_to_file(char *path, unsigned long bytes);
/**
+ * @brief Writes random to a file at a given seek offset
+ *
+ * @param path The file path
+ * @param bytes The amount of bytes
+ * @param offset The seek offset in bytes
+ */
+void write_random_to_file(char *path, unsigned long bytes, off_t offset);
+
+/**
* @brief Ensures that a directory exists
*
* @param path The directory path
diff --git a/ioriot/src/utils/utils.c b/ioriot/src/utils/utils.c
index 4b41273..ae3549f 100644
--- a/ioriot/src/utils/utils.c
+++ b/ioriot/src/utils/utils.c
@@ -90,32 +90,34 @@ void set_limits_drop_root(const char *user)
rl.rlim_cur = rl.rlim_max = SET_RLIMIT_NOFILE;
if (0 != setrlimit(RLIMIT_NOFILE, &rl)) {
Errno("Could not set RLIMIT_NOFILE to '%lld'!",
- (long long) SET_RLIMIT_NOFILE)
+ (long long) SET_RLIMIT_NOFILE)
}
rl.rlim_cur = rl.rlim_max = SET_RLIMIT_NPROC;
if (0 != setrlimit(RLIMIT_NPROC, &rl)) {
Errno("Could not set RLIMIT_NPROC to '%lld'!",
- (long long) SET_RLIMIT_NPROC)
+ (long long) SET_RLIMIT_NPROC)
}
- Put("Dropping root privileges to user '%s'", user);
- struct passwd *pw = getpwnam(user);
-
- /* process is running as root, drop privileges */
- if (setgid(pw->pw_gid) != 0) {
- Errno("Unable to drop group privileges!");
- }
- if (setuid(pw->pw_uid) != 0) {
- Errno("Unable to drop user privileges!");
+ if (!Eq("root", user)) {
+ Put("Dropping root privileges to user '%s'", user);
+ struct passwd *pw = getpwnam(user);
+
+ /* process is running as root, drop privileges */
+ if (setgid(pw->pw_gid) != 0) {
+ Errno("Unable to drop group privileges!");
+ }
+ if (setuid(pw->pw_uid) != 0) {
+ Errno("Unable to drop user privileges!");
+ }
}
}
/*
- getrlimit(RLIMIT_NOFILE, &rl);
- Put("Max open files: '%lld'", (long long) rl.rlim_cur);
- getrlimit(RLIMIT_NPROC, &rl);
- Put("Max open processes : '%lld'", (long long) rl.rlim_cur);
- */
+ getrlimit(RLIMIT_NOFILE, &rl);
+ Put("Max open files: '%lld'", (long long) rl.rlim_cur);
+ getrlimit(RLIMIT_NPROC, &rl);
+ Put("Max open processes : '%lld'", (long long) rl.rlim_cur);
+ */
}
void get_loadavg_s(char *readbuf)
diff --git a/systemtap/src/ioriot.stp b/systemtap/src/ioriot.stp
index 9ea6a68..ee77263 100644
--- a/systemtap/src/ioriot.stp
+++ b/systemtap/src/ioriot.stp
@@ -71,6 +71,15 @@ function absolute_path (path) {
return task_dentry_path(tc, pwd_dentry, pwd_mnt) . "/" . path;
}
+# Stop probing after 1h (for safety)
+probe timer.s(3600) {
+ exit();
+}
+
+probe begin {
+ printf("#|capture_version=%d|\n", 2);
+}
+
probe syscall.open.return, syscall.openat.return {
if (execname() != "stapio") {
pathname = user_string(@entry($filename))
@@ -100,6 +109,20 @@ probe syscall.lseek.return {
}
}
+probe syscall.llseek.return {
+ if(execname() != "stapio") {
+ printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,O=%d;:,W=%d;:,b=%d;:,\n",
+ gettimeofday_ms(),
+ pid(),
+ tid(),
+ name,
+ @entry($fd),
+ (@entry($offset_high) << 32 | @entry($offset_low)),
+ @entry($whence),
+ $return);
+ }
+}
+
probe syscall.fcntl.return {
if(execname() != "stapio") {
printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,F=%d;:,G=%d;:,s=%d;:,\n",
@@ -583,9 +606,4 @@ probe syscall.exit_group {
}
}
-# Stop probing after 1h (for safety)
-probe timer.s(3600) {
- exit();
-}
-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
diff --git a/systemtap/src/javaioriot.stp b/systemtap/src/javaioriot.stp
index 3943971..b0e960c 100644
--- a/systemtap/src/javaioriot.stp
+++ b/systemtap/src/javaioriot.stp
@@ -71,6 +71,15 @@ function absolute_path (path) {
return task_dentry_path(tc, pwd_dentry, pwd_mnt) . "/" . path;
}
+# Stop probing after 1h (for safety)
+probe timer.s(3600) {
+ exit();
+}
+
+probe begin {
+ printf("#|capture_version=%d|\n", 2);
+}
+
probe syscall.open.return, syscall.openat.return {
if (execname() == "java") {
pathname = user_string(@entry($filename))
@@ -100,6 +109,20 @@ probe syscall.lseek.return {
}
}
+probe syscall.llseek.return {
+ if(execname() == "java") {
+ printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,O=%d;:,W=%d;:,b=%d;:,\n",
+ gettimeofday_ms(),
+ pid(),
+ tid(),
+ name,
+ @entry($fd),
+ (@entry($offset_high) << 32 | @entry($offset_low)),
+ @entry($whence),
+ $return);
+ }
+}
+
probe syscall.fcntl.return {
if(execname() == "java") {
printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,F=%d;:,G=%d;:,s=%d;:,\n",
@@ -583,9 +606,4 @@ probe syscall.exit_group {
}
}
-# Stop probing after 1h (for safety)
-probe timer.s(3600) {
- exit();
-}
-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
diff --git a/systemtap/src/targetedioriot.stp b/systemtap/src/targetedioriot.stp
index f72dc66..539b826 100644
--- a/systemtap/src/targetedioriot.stp
+++ b/systemtap/src/targetedioriot.stp
@@ -71,6 +71,15 @@ function absolute_path (path) {
return task_dentry_path(tc, pwd_dentry, pwd_mnt) . "/" . path;
}
+# Stop probing after 1h (for safety)
+probe timer.s(3600) {
+ exit();
+}
+
+probe begin {
+ printf("#|capture_version=%d|\n", 2);
+}
+
probe syscall.open.return, syscall.openat.return {
if (pid() == target()) {
pathname = user_string(@entry($filename))
@@ -100,6 +109,20 @@ probe syscall.lseek.return {
}
}
+probe syscall.llseek.return {
+ if(pid() == target()) {
+ printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,O=%d;:,W=%d;:,b=%d;:,\n",
+ gettimeofday_ms(),
+ pid(),
+ tid(),
+ name,
+ @entry($fd),
+ (@entry($offset_high) << 32 | @entry($offset_low)),
+ @entry($whence),
+ $return);
+ }
+}
+
probe syscall.fcntl.return {
if(pid() == target()) {
printf("t=%d;:,i=%d:%d;:,o=%s;:,d=%d;:,F=%d;:,G=%d;:,s=%d;:,\n",
@@ -583,9 +606,4 @@ probe syscall.exit_group {
}
}
-# Stop probing after 1h (for safety)
-probe timer.s(3600) {
- exit();
-}
-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4