diff options
| author | Paul Bütow <pbuetow@mimecast.com> | 2018-03-30 12:38:57 +0100 |
|---|---|---|
| committer | Paul Bütow <pbuetow@mimecast.com> | 2018-03-30 12:38:57 +0100 |
| commit | 0bf0f8cc86cab23668e45fbca3a4f46aa87cfe88 (patch) | |
| tree | 6002540e7902278dc04eeb9950fd1f0685b1b377 | |
| parent | 49612db2713ccce2022495814c724e290c0e2b0f (diff) | |
| parent | 79b99c0ad4c1761b4da71721d18c94648fb64d32 (diff) | |
Merge branch 'develop'0.4
30 files changed, 376 insertions, 158 deletions
@@ -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 @@ -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 |
