summaryrefslogtreecommitdiff
path: root/ioshark
diff options
context:
space:
mode:
authorMohan Srinivasan <srmohan@google.com>2016-12-21 22:18:13 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2016-12-21 22:18:13 +0000
commit0d69af9d9a24059151eb8ee09a343bd4155b7e12 (patch)
tree0b1e42246bdb67c127ba075f62d33055269139a3 /ioshark
parent325bc98318284a3039ff91e79fb6dbac5ed49a0d (diff)
parent99c8f98f2a9c76fabb19cdbc5b495978cfc479b3 (diff)
downloadextras-0d69af9d9a24059151eb8ee09a343bd4155b7e12.tar.gz
Merge "ioshark: A Repeatable Application Workload Based Storage Benchmark."
Diffstat (limited to 'ioshark')
-rw-r--r--ioshark/Android.mk30
-rw-r--r--ioshark/README26
-rwxr-xr-xioshark/collect-straces.sh94
-rw-r--r--ioshark/compile_ioshark.c578
-rw-r--r--ioshark/compile_ioshark.h78
-rw-r--r--ioshark/compile_ioshark_subr.c102
-rw-r--r--ioshark/ioshark.h108
-rw-r--r--ioshark/ioshark_bench.c745
-rw-r--r--ioshark/ioshark_bench.h151
-rw-r--r--ioshark/ioshark_bench_mmap.c213
-rw-r--r--ioshark/ioshark_bench_subr.c324
11 files changed, 2449 insertions, 0 deletions
diff --git a/ioshark/Android.mk b/ioshark/Android.mk
new file mode 100644
index 00000000..a2e4816d
--- /dev/null
+++ b/ioshark/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ioshark_bench.c ioshark_bench_subr.c ioshark_bench_mmap.c
+LOCAL_CFLAGS := -g -O2 -Wall -Werror
+LOCAL_MODULE := ioshark_bench
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := compile_ioshark.c compile_ioshark_subr.c
+LOCAL_CFLAGS := -g -O2 -Wall -Werror -D_GNU_SOURCE
+LOCAL_MODULE := compile_ioshark
+include $(BUILD_HOST_EXECUTABLE)
+
+
+
diff --git a/ioshark/README b/ioshark/README
new file mode 100644
index 00000000..3afbe76e
--- /dev/null
+++ b/ioshark/README
@@ -0,0 +1,26 @@
+IOshark is a repeatable application workload storage benchmark. You
+can find more documentation on IOshark at :
+https://docs.google.com/a/google.com/document/d/1Bhq7iNPVc_JzwRrkmZqcPjMvWgpHX0r3Ncq-ZsRNOBA/edit?usp=sharing
+
+The short summary of what IOshark is : IOshark has 2 components, one
+is a strace compiler that takes straces fed into it and compiles this
+into bytecodes (stored in *.wl files). The compiler runs on a Linux
+host. The second component (which runs on the device) is the tester
+that takes as input the bytecode files (*.wl files) and executes them
+on the device.
+
+How to Run :
+----------
+- First collect straces and compile these into bytecodes. The wrapper
+script provided (collect-straces.sh) collects straces, ships them to
+the host where the script runs, compiles and packages up the bytecode
+files into a wl.tar file.
+- Ship the wl.tar file and the iostark_bench binaries to the target
+device (on /data/local/tmp say). Explode the tarfile.
+- Run the tester. "ioshark_bench *.wl" runs the test with default
+options. Supported ioshark_bench options :
+-d : Preserve the delays between successive filesystem syscalls as
+seen in the original straces.
+-n <N> : Run for N iterations
+-t <N> : Limit to N threads. By default (without this option), IOshark
+will launch as many threads as there are input files, so 1 thread/file. \ No newline at end of file
diff --git a/ioshark/collect-straces.sh b/ioshark/collect-straces.sh
new file mode 100755
index 00000000..4577e736
--- /dev/null
+++ b/ioshark/collect-straces.sh
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#!/bin/sh
+
+# When signal is received, the stracer will get killed
+# Call this (just to make sure anyway)
+kill_strace() {
+ ps_line=`ps -ef | grep strace | grep adb `
+ if [ $? == 0 ]; then
+ echo Killing `echo $ps_line | awk '{s = ""; for (i=8; i <= NF ; i++) s = s \
+$i " "; print s}' `
+ kill `echo $ps_line | awk '{print $2}' `
+ fi
+}
+
+catch_sigint()
+{
+ echo "signal INT received, killing streaming trace capture"
+ kill_strace
+}
+
+compile_tracefiles()
+{
+ for i in trace.*
+ do
+ if [ $i != trace.begin ] && [ $i != trace.tar ];
+ then
+ egrep '\/system\/|\/data\/|\/vendor\/' $i > bar
+# parse out /sys/devices/system/...
+ egrep -v '\/sys\/devices\/system\/' bar > bar0
+ mv bar0 bar
+ fgrep -v '= -1' bar > foo
+ mv foo bar
+ if [ -s bar ]
+ then
+ echo parsing $i
+ compile_ioshark bar $i.wl
+ rm -f bar
+ else
+ rm -f $i bar
+ fi
+ fi
+ done
+}
+
+# main() starts here
+
+adb root && adb wait-for-device
+
+adb shell 'ps -ef' | grep zygote > zygote_pids
+fgrep -v grep zygote_pids > bar
+mv bar zygote_pids
+pid1=`grep -w zygote zygote_pids | awk '{print $2}' `
+pid2=`grep -w zygote64 zygote_pids | awk '{print $2}' `
+rm -f zygote_pids
+
+trap 'catch_sigint' INT
+
+echo "^C this script once you finish running your test"
+
+adb shell "date +%s > /data/local/tmp/trace.begin ; strace -p $pid1,$pid2 -o /data/local/tmp/trace -q -qq -f -ff -y -ttt -e trace=mmap2,read,write,pread64,pwrite64,fsync,fdatasync,openat,close,lseek,_llseek"
+
+# Remove any remnant tracefiles first
+rm -f trace.*
+
+# Get the tracefiles from the device
+adb shell 'cd /data/local/tmp ; tar cvf trace.tar trace.*'
+adb pull /data/local/tmp/trace.tar
+
+# Extract the tracefiles from the device
+rm -f *.wl
+tar xf trace.tar
+
+# Compile the tracefiles
+compile_tracefiles
+
+# tar up the .wl files just created
+tar zcf wl.tgz *.wl
+
+
diff --git a/ioshark/compile_ioshark.c b/ioshark/compile_ioshark.c
new file mode 100644
index 00000000..609d2bc2
--- /dev/null
+++ b/ioshark/compile_ioshark.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <linux/fs.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "ioshark.h"
+#include "compile_ioshark.h"
+
+char *progname;
+
+char in_buf[2048];
+
+struct flags_map_s {
+ char *flag_str;
+ int flag;
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+struct flags_map_s open_flags_map[] = {
+ { "O_RDONLY", O_RDONLY },
+ { "O_WRONLY", O_WRONLY },
+ { "O_RDWR", O_RDWR },
+ { "O_CREAT", O_CREAT },
+ { "O_SYNC", O_SYNC },
+ { "O_TRUNC", O_TRUNC },
+ { "O_EXCL", O_EXCL },
+ { "O_APPEND", O_APPEND },
+ { "O_NOATIME", O_NOATIME },
+ { "O_ASYNC", O_ASYNC },
+ { "O_CLOEXEC", O_CLOEXEC },
+ { "O_DIRECT", O_DIRECT },
+ { "O_DIRECTORY", O_DIRECTORY },
+ { "O_LARGEFILE", O_LARGEFILE },
+ { "O_NOCTTY", O_NOCTTY },
+ { "O_NOFOLLOW", O_NOFOLLOW },
+ { "O_NONBLOCK", O_NONBLOCK },
+ { "O_NDELAY", O_NDELAY },
+ { "O_PATH", O_PATH }
+};
+
+struct flags_map_s lseek_action_map[] = {
+ { "SEEK_SET", SEEK_SET },
+ { "SEEK_CUR", SEEK_CUR },
+ { "SEEK_END", SEEK_END }
+};
+
+struct flags_map_s fileop_map[] = {
+ { "lseek", IOSHARK_LSEEK },
+ { "_llseek", IOSHARK_LLSEEK },
+ { "pread64", IOSHARK_PREAD64 },
+ { "pwrite64", IOSHARK_PWRITE64 },
+ { "read", IOSHARK_READ },
+ { "write", IOSHARK_WRITE },
+ { "mmap", IOSHARK_MMAP },
+ { "mmap2", IOSHARK_MMAP2 },
+ { "openat", IOSHARK_OPEN },
+ { "fsync", IOSHARK_FSYNC },
+ { "fdatasync", IOSHARK_FDATASYNC },
+ { "close", IOSHARK_CLOSE }
+};
+
+struct in_mem_file_op {
+ struct ioshark_file_operation disk_file_op;
+ struct in_mem_file_op *next;
+};
+
+struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL;
+
+void usage(void)
+{
+ fprintf(stderr, "%s in_file out_file\n", progname);
+}
+
+u_int64_t
+get_delta_ts(char *buf, struct timeval *start_tv)
+{
+ struct timeval op_tv, tv_res;
+
+ sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec);
+ timersub(&op_tv, start_tv, &tv_res);
+ return (tv_res.tv_usec + (tv_res.tv_sec * 1000000));
+}
+
+void
+get_pathname(char *buf, char *pathname, enum file_op file_op)
+{
+ char *s, *s2, save;
+
+ if (file_op == IOSHARK_OPEN)
+ s = strchr(buf, '"');
+ else
+ s = strchr(buf, '<');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 1;
+ if (file_op == IOSHARK_OPEN)
+ s2 = strchr(s, '"');
+ else
+ s2 = strchr(s, '>');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ save = *s2;
+ *s2 = '\0';
+ strcpy(pathname, s);
+ *s2 = save;
+}
+
+void
+get_syscall(char *buf, char *syscall)
+{
+ char *s, *s2;
+
+ s = strchr(buf, ' ');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 1;
+ s2 = strchr(s, '(');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(syscall, s);
+ *s2 = '(';
+}
+
+void
+get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len)
+{
+ char *s, *s1;
+ int i;
+ char protstr[128];
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ sscanf(s, "%lu", len);
+ s1 = strchr(s, ',');
+ if (s1 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s1 += 2;
+ s = strchr(s1, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s = '\0';
+ strcpy(protstr, s1);
+ *prot = 0;
+ if (strstr(protstr, "PROT_READ"))
+ *prot |= IOSHARK_PROT_READ;
+ if (strstr(protstr, "PROT_WRITE"))
+ *prot |= IOSHARK_PROT_WRITE;
+ *s = ',';
+ s += 2;
+ for (i = 0 ; i < 2 ; i++) {
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ }
+ sscanf(s, "%lx", offset);
+}
+
+void
+get_lseek_offset_action(char *buf, enum file_op op,
+ off_t *offset, char *action)
+{
+ char *s, *s2;
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ sscanf(s, "%lu", offset);
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ if (op == IOSHARK_LLSEEK) {
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ }
+ s2 = strchr(s, ')');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(action, s);
+ *s2 = ')';
+}
+
+void
+get_rw_len(char *buf,
+ u_int64_t *len)
+{
+ char *s_len;
+
+ s_len = strrchr(buf, ',');
+ if (s_len == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ sscanf(s_len + 2, "%lu", len);
+}
+
+void
+get_prw64_offset_len(char *buf,
+ off_t *offset,
+ u_int64_t *len)
+{
+ char *s_offset, *s_len;
+
+ s_offset = strrchr(buf, ',');
+ if (s_offset == NULL) {
+ fprintf(stderr, "%s: Malformed line 1: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s_offset = '\0';
+ s_len = strrchr(buf, ',');
+ if (s_len == NULL) {
+ fprintf(stderr, "%s: Malformed line 2: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s_offset = ',';
+ sscanf(s_len + 2, "%lu", len);
+ sscanf(s_offset + 2, "%lu", offset);
+}
+
+void
+get_openat_flags_mode(char *buf, char *flags, mode_t *mode)
+{
+ char *s, *s2, lookfor;
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ if (strstr(s, "O_CREAT") == NULL)
+ lookfor = ')';
+ else
+ lookfor = ',';
+ s2 = strchr(s, lookfor);
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(flags, s);
+ *s2 = lookfor;
+ if (strstr(s, "O_CREAT") != NULL) {
+ s = s2 + 2;
+ s2 = strchr(s, ')');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ sscanf(s, "%o", mode);
+ *s2 = ')';
+ }
+}
+
+int
+lookup_map(char *s, struct flags_map_s *flags_map, int maplen)
+{
+ int found = 0, flag = 0;
+ int i;
+
+ while (isspace(*s))
+ s++;
+ for (i = 0 ; i < maplen ; i++) {
+ if (strcmp(flags_map[i].flag_str, s) == 0) {
+ flag = flags_map[i].flag;
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0) {
+ fprintf(stderr, "%s: Unknown syscall %s\n",
+ __func__, s);
+ exit(1);
+ }
+ return flag;
+}
+
+int
+map_open_flags(char *s)
+{
+ int flags = 0;
+ char *s1;
+
+ while ((s1 = strchr(s, '|'))) {
+ *s1 = '\0';
+ flags |= lookup_map(s, open_flags_map,
+ ARRAY_SIZE(open_flags_map));
+ *s1 = '|';
+ s = s1 + 1;
+ }
+ /* Last option */
+ flags |= lookup_map(s, open_flags_map,
+ ARRAY_SIZE(open_flags_map));
+ return flags;
+}
+
+int
+map_lseek_action(char *s)
+{
+ int flags = 0;
+ char *s1;
+
+ while ((s1 = strchr(s, '|'))) {
+ *s1 = '\0';
+ flags |= lookup_map(s, lseek_action_map,
+ ARRAY_SIZE(lseek_action_map));
+ *s1 = '|';
+ s = s1 + 1;
+ }
+ /* Last option */
+ flags |= lookup_map(s, lseek_action_map,
+ ARRAY_SIZE(lseek_action_map));
+ return flags;
+}
+
+enum file_op
+map_syscall(char *syscall)
+{
+ return lookup_map(syscall, fileop_map,
+ ARRAY_SIZE(fileop_map));
+}
+
+void
+get_start_time(struct timeval *tv)
+{
+ FILE *fp;
+
+ tv->tv_usec = 0;
+ fp = fopen("trace.begin", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open trace.begin\n",
+ __func__);
+ exit(EXIT_FAILURE);
+ }
+ if (fscanf(fp, "%lu", &tv->tv_sec) != 1) {
+ fprintf(stderr, "%s Can't read seconds from trace.begin\n",
+ __func__);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * For each tracefile, we first create in-memory structures, then once
+ * we've processed each tracefile completely, we write out the in-memory
+ * structures and free them.
+ */
+int main(int argc, char **argv)
+{
+ FILE *fp;
+ char path[512];
+ char syscall[512];
+ char lseek_action_str[512];
+ char *s;
+ char open_flags_str[64];
+ void *db_node;
+ int num_io_operations = 0;
+ struct ioshark_header header;
+ struct ioshark_file_operation *disk_file_op;
+ struct in_mem_file_op *in_mem_fop;
+ struct stat st;
+ char *infile, *outfile;
+ struct timeval start_time;
+
+ progname = argv[0];
+ if (argc != 3) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ infile = argv[1];
+ outfile = argv[2];
+ if (stat(infile, &st) < 0) {
+ fprintf(stderr, "%s Can't stat %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ if (st.st_size == 0) {
+ fprintf(stderr, "%s Empty file %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ get_start_time(&start_time);
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ while (fgets(in_buf, 2048, fp)) {
+ s = in_buf;
+ while (isspace(*s))
+ s++;
+ in_mem_fop = malloc(sizeof(struct in_mem_file_op));
+ disk_file_op = &in_mem_fop->disk_file_op;
+ disk_file_op->delta_us = get_delta_ts(s, &start_time);
+ get_syscall(s, syscall);
+ disk_file_op->file_op = map_syscall(syscall);
+ get_pathname(s, path, disk_file_op->file_op);
+ db_node = files_db_add(path);
+ disk_file_op->fileno = files_db_get_fileno(db_node);
+ switch (disk_file_op->file_op) {
+ case IOSHARK_LLSEEK:
+ case IOSHARK_LSEEK:
+ get_lseek_offset_action(s,
+ disk_file_op->file_op,
+ &disk_file_op->lseek_offset,
+ lseek_action_str);
+ disk_file_op->lseek_action =
+ map_lseek_action(lseek_action_str);
+ if (disk_file_op->lseek_action == SEEK_SET)
+ files_db_update_size(db_node,
+ disk_file_op->lseek_offset);
+ break;
+ case IOSHARK_PREAD64:
+ case IOSHARK_PWRITE64:
+ get_prw64_offset_len(s,
+ &disk_file_op->prw_offset,
+ &disk_file_op->prw_len);
+ files_db_update_size(db_node,
+ disk_file_op->prw_offset +
+ disk_file_op->prw_len);
+ break;
+ case IOSHARK_READ:
+ case IOSHARK_WRITE:
+ get_rw_len(s, &disk_file_op->rw_len);
+ files_db_add_to_size(db_node,
+ disk_file_op->rw_len);
+ break;
+ case IOSHARK_MMAP:
+ case IOSHARK_MMAP2:
+ get_mmap_offset_len_prot(s,
+ &disk_file_op->mmap_prot,
+ &disk_file_op->mmap_offset,
+ &disk_file_op->mmap_len);
+ files_db_update_size(db_node,
+ disk_file_op->mmap_offset +
+ disk_file_op->mmap_len);
+ break;
+ case IOSHARK_OPEN:
+ disk_file_op->open_mode = 0;
+ get_openat_flags_mode(s, open_flags_str,
+ &disk_file_op->open_mode);
+ disk_file_op->open_flags =
+ map_open_flags(open_flags_str);
+ break;
+ case IOSHARK_FSYNC:
+ case IOSHARK_FDATASYNC:
+ break;
+ case IOSHARK_CLOSE:
+ break;
+ default:
+ break;
+ }
+ /* Chain at the end */
+ num_io_operations++;
+ in_mem_fop->next = NULL;
+ if (in_mem_file_op_head == NULL) {
+ in_mem_file_op_tail = in_mem_fop;
+ in_mem_file_op_head = in_mem_fop;
+ } else {
+ in_mem_file_op_tail->next = in_mem_fop;
+ in_mem_file_op_tail = in_mem_fop;
+ }
+ }
+ fclose(fp);
+ /*
+ * Now we can write everything out to the output tracefile.
+ */
+ fp = fopen(outfile, "w+");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ header.num_io_operations = num_io_operations;
+ header.num_files = files_db_get_total_obj();
+ if (fwrite(&header, sizeof(struct ioshark_header), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ files_db_write_objects(fp);
+ while (in_mem_file_op_head != NULL) {
+ struct in_mem_file_op *temp;
+
+ disk_file_op = &in_mem_file_op_head->disk_file_op;
+ if (fwrite(disk_file_op,
+ sizeof(struct ioshark_file_operation), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ temp = in_mem_file_op_head;
+ in_mem_file_op_head = in_mem_file_op_head->next;
+ free(temp);
+ }
+}
diff --git a/ioshark/compile_ioshark.h b/ioshark/compile_ioshark.h
new file mode 100644
index 00000000..13095bf6
--- /dev/null
+++ b/ioshark/compile_ioshark.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define FILE_DB_HASHSIZE 8192
+
+struct files_db_s {
+ char *filename;
+ int fileno;
+ struct files_db_s *next;
+ size_t size;
+};
+
+/* Lifted from Wikipedia Jenkins Hash function page */
+static inline u_int32_t
+jenkins_one_at_a_time_hash(char *key, size_t len)
+{
+ u_int32_t hash, i;
+
+ for(hash = i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+static inline void
+files_db_update_size(void *node, u_int64_t new_size)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->size < new_size)
+ db_node->size = new_size;
+}
+
+static inline void
+files_db_add_to_size(void *node, u_int64_t size_incr)
+{
+ ((struct files_db_s *)node)->size += size_incr;
+}
+
+static inline int
+files_db_get_fileno(void *node)
+{
+ return (((struct files_db_s *)node)->fileno);
+}
+
+static inline char *
+files_db_get_filename(void *node)
+{
+ return (((struct files_db_s *)node)->filename);
+}
+
+
+void *files_db_create_handle(void);
+void files_db_write_objects(FILE *fp);
+void *files_db_add(char *filename);
+void *files_db_lookup(char *filename);
+int files_db_get_total_obj(void);
+
+
+
diff --git a/ioshark/compile_ioshark_subr.c b/ioshark/compile_ioshark_subr.c
new file mode 100644
index 00000000..e2b6b891
--- /dev/null
+++ b/ioshark/compile_ioshark_subr.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include "ioshark.h"
+#include "compile_ioshark.h"
+
+extern char *progname;
+
+static struct files_db_s *files_db_buckets[FILE_DB_HASHSIZE];
+static int current_fileno = 1;
+static int num_objects = 0;
+
+void
+files_db_write_objects(FILE *fp)
+{
+ int i;
+ struct ioshark_file_state st;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ struct files_db_s *db_node, *s;
+
+ db_node = files_db_buckets[i];
+ while (db_node != NULL) {
+ st.fileno = db_node->fileno;
+ st.size = db_node->size;
+ if (fwrite(&st, sizeof(st), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ s = db_node;
+ db_node = db_node->next;
+ free(s->filename);
+ free(s);
+ }
+ }
+}
+
+void *files_db_lookup(char *pathname)
+{
+ u_int32_t hash;
+ struct files_db_s *db_node;
+
+ hash = jenkins_one_at_a_time_hash(pathname, strlen(pathname));
+ hash %= FILE_DB_HASHSIZE;
+ db_node = files_db_buckets[hash];
+ while (db_node != NULL) {
+ if (strcmp(db_node->filename, pathname) == 0)
+ break;
+ db_node = db_node->next;
+ }
+ return db_node;
+}
+
+void *files_db_add(char *filename)
+{
+ u_int32_t hash;
+ struct files_db_s *db_node;
+
+ if ((db_node = files_db_lookup(filename)))
+ return db_node;
+ hash = jenkins_one_at_a_time_hash(filename, strlen(filename));
+ hash %= FILE_DB_HASHSIZE;
+ db_node = malloc(sizeof(struct files_db_s));
+ db_node->filename = strdup(filename);
+ db_node->fileno = current_fileno++;
+ db_node->next = files_db_buckets[hash];
+ db_node->size = 0;
+ files_db_buckets[hash] = db_node;
+ num_objects++;
+ return db_node;
+}
+
+int
+files_db_get_total_obj(void)
+{
+ return num_objects;
+}
+
+
+
diff --git a/ioshark/ioshark.h b/ioshark/ioshark.h
new file mode 100644
index 00000000..f0a715f7
--- /dev/null
+++ b/ioshark/ioshark.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Format of the parsed workload files.
+ * 1) Header
+ * 2) Table of the entries, each entry describes 1 file
+ * 3) Table of IO operations to perform on the files
+ */
+
+/*
+ * The parsed workload file starts off with the header, which
+ * contains the count of the total # of files that are operated on.
+ * and the total number of IO operations.
+ */
+struct ioshark_header {
+ int num_files;
+ int num_io_operations;
+};
+
+/*
+ * After the header, we have a table of #files entries. Each entry
+ * in this table describes 1 file, indexed by fileno and with the
+ * specified size.
+ * Before the tests starts, these files are pre-created.
+ */
+struct ioshark_file_state {
+ int fileno; /* 1..num_files, with files name ioshark.<fileno> */
+ size_t size;
+};
+
+enum file_op {
+ IOSHARK_LSEEK = 0,
+ IOSHARK_LLSEEK,
+ IOSHARK_PREAD64,
+ IOSHARK_PWRITE64,
+ IOSHARK_READ,
+ IOSHARK_WRITE,
+ IOSHARK_MMAP,
+ IOSHARK_MMAP2,
+ IOSHARK_OPEN,
+ IOSHARK_FSYNC,
+ IOSHARK_FDATASYNC,
+ IOSHARK_CLOSE,
+ IOSHARK_MAPPED_PREAD,
+ IOSHARK_MAPPED_PWRITE,
+ IOSHARK_MAX_FILE_OP
+};
+
+/* mmap prot flags */
+#define IOSHARK_PROT_READ 0x1
+#define IOSHARK_PROT_WRITE 0x2
+
+/*
+ * Next we have the table of IO operatiosn to perform. Each
+ * IO operation is described by this entry.
+ */
+struct ioshark_file_operation {
+ /* delta us between previous file op and this */
+ u_int64_t delta_us;
+ enum file_op file_op;
+ int fileno;
+ union {
+ struct lseek_args {
+#define lseek_offset u.lseek_a.offset
+#define lseek_action u.lseek_a.action
+ off_t offset;
+ int action;
+ } lseek_a;
+ struct prw_args {
+#define prw_offset u.prw_a.offset
+#define prw_len u.prw_a.len
+ off_t offset;
+ size_t len;
+ } prw_a;
+#define rw_len u.rw_a.len
+ struct rw_args {
+ size_t len;
+ } rw_a;
+#define mmap_offset u.mmap_a.offset
+#define mmap_len u.mmap_a.len
+#define mmap_prot u.mmap_a.prot
+ struct mmap_args {
+ off_t offset;
+ size_t len;
+ int prot;
+ } mmap_a;
+#define open_flags u.open_a.flags
+#define open_mode u.open_a.mode
+ struct open_args {
+ int flags;
+ mode_t mode;
+ } open_a;
+ } u;
+};
diff --git a/ioshark/ioshark_bench.c b/ioshark/ioshark_bench.c
new file mode 100644
index 00000000..9bbfcae1
--- /dev/null
+++ b/ioshark/ioshark_bench.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <linux/fs.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/statfs.h>
+#include "ioshark.h"
+#define IOSHARK_MAIN
+#include "ioshark_bench.h"
+
+#define UNUSED_PARAM(X) ((void)X)
+
+char *progname;
+
+#define MAX_INPUT_FILES 1024
+#define MAX_THREADS 1024
+
+struct thread_state_s {
+ char *filename;
+ FILE *fp;
+ int num_files;
+ void *db_handle;
+};
+
+struct thread_state_s thread_state[MAX_INPUT_FILES];
+int num_input_files = 0;
+int next_input_file;
+
+pthread_t tid[MAX_THREADS];
+
+/*
+ * Global options
+ */
+int do_delay = 0;
+
+#if 0
+static long gettid()
+{
+ return syscall(__NR_gettid);
+}
+#endif
+
+void usage()
+{
+ fprintf(stderr, "%s [-d preserve_delays] [-n num_iterations] [-t num_threads] <list of parsed input files>\n",
+ progname);
+ exit(EXIT_FAILURE);
+}
+
+pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER;
+struct timeval aggregate_file_create_time;
+struct timeval aggregate_file_remove_time;
+struct timeval aggregate_IO_time;
+struct timeval aggregate_delay_time;
+
+u_int64_t aggr_op_counts[IOSHARK_MAX_FILE_OP];
+struct rw_bytes_s aggr_io_rw_bytes;
+struct rw_bytes_s aggr_create_rw_bytes;
+
+static void
+update_time(struct timeval *aggr_time,
+ struct timeval *delta_time)
+{
+ struct timeval tmp;
+
+ pthread_mutex_lock(&time_mutex);
+ timeradd(aggr_time, delta_time, &tmp);
+ *aggr_time = tmp;
+ pthread_mutex_unlock(&time_mutex);
+}
+
+static void
+update_op_counts(u_int64_t *op_counts)
+{
+ int i;
+
+ pthread_mutex_lock(&stats_mutex);
+ for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++)
+ aggr_op_counts[i] += op_counts[i];
+ pthread_mutex_unlock(&stats_mutex);
+}
+
+static void
+update_byte_counts(struct rw_bytes_s *dest, struct rw_bytes_s *delta)
+{
+ pthread_mutex_lock(&stats_mutex);
+ dest->bytes_read += delta->bytes_read;
+ dest->bytes_written += delta->bytes_written;
+ pthread_mutex_unlock(&stats_mutex);
+}
+
+static int work_next_file;
+static int work_num_files;
+
+void
+init_work(int next_file, int num_files)
+{
+ pthread_mutex_lock(&work_mutex);
+ work_next_file = next_file;
+ work_num_files = work_next_file + num_files;
+ pthread_mutex_unlock(&work_mutex);
+}
+
+/* Dole out the next file to work on to the thread */
+static struct thread_state_s *
+get_work()
+{
+ struct thread_state_s *work = NULL;
+
+ pthread_mutex_lock(&work_mutex);
+ if (work_next_file < work_num_files)
+ work = &thread_state[work_next_file++];
+ pthread_mutex_unlock(&work_mutex);
+ return work;
+}
+
+static void
+create_files(struct thread_state_s *state)
+{
+ struct timeval file_create_time;
+ int i;
+ struct ioshark_file_state file_state;
+ char path[512];
+ void *db_node;
+ struct rw_bytes_s rw_bytes;
+
+ timerclear(&file_create_time);
+ memset(&rw_bytes, 0, sizeof(struct rw_bytes_s));
+ for (i = 0 ; i < state->num_files ; i++) {
+ if (fread(&file_state, sizeof(struct ioshark_file_state),
+ 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error tracefile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ sprintf(path, "file.%d.%d",
+ (int)(state - thread_state),
+ file_state.fileno);
+ create_file(path, file_state.size,
+ &file_create_time, &rw_bytes);
+ db_node = files_db_add_byfileno(state->db_handle,
+ file_state.fileno);
+ files_db_update_size(db_node, file_state.size);
+ files_db_update_filename(db_node, path);
+ }
+ update_byte_counts(&aggr_create_rw_bytes, &rw_bytes);
+ update_time(&aggregate_file_create_time, &file_create_time);
+}
+
+static void
+do_one_io(void *db_node,
+ struct ioshark_file_operation *file_op,
+ struct timeval *io_time, u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes,
+ char **bufp, int *buflen)
+{
+ struct timeval start;
+
+ assert(file_op->file_op < IOSHARK_MAX_FILE_OP);
+ op_counts[file_op->file_op]++;
+ switch (file_op->file_op) {
+ int ret;
+ char *p;
+ int fd;
+
+ case IOSHARK_LSEEK:
+ case IOSHARK_LLSEEK:
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ ret = lseek(files_db_get_fd(db_node),
+ file_op->lseek_offset,
+ file_op->lseek_action);
+ update_delta_time(&start, io_time);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: lseek(%s %lu %d) returned error %d\n",
+ progname, files_db_get_filename(db_node),
+ file_op->lseek_offset,
+ file_op->lseek_action, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_PREAD64:
+ p = get_buf(bufp, buflen, file_op->prw_len, 0);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = pread(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_read += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pread(%s %lu %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->prw_len,
+ file_op->prw_offset, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_PWRITE64:
+ p = get_buf(bufp, buflen, file_op->prw_len, 1);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = pwrite(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_written += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pwrite(%s %lu %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->prw_len,
+ file_op->prw_offset, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_READ:
+ p = get_buf(bufp, buflen, file_op->rw_len, 0);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = read(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_read += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: read(%s %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->rw_len,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_WRITE:
+ p = get_buf(bufp, buflen, file_op->rw_len, 1);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = write(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_written += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: write(%s %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->rw_len,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_MMAP:
+ case IOSHARK_MMAP2:
+ ioshark_handle_mmap(db_node, file_op,
+ bufp, buflen, op_counts,
+ rw_bytes, io_time);
+ break;
+ case IOSHARK_OPEN:
+ if (file_op->open_flags & O_CREAT) {
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ fd = open(files_db_get_filename(db_node),
+ file_op->open_flags,
+ file_op->open_mode);
+ if (fd < 0) {
+ /*
+ * EEXIST error acceptable, others are fatal.
+ * Although we failed to O_CREAT the file (O_EXCL)
+ * We will force an open of the file before any
+ * IO.
+ */
+ if (errno == EEXIST) {
+ update_delta_time(&start, io_time);
+ return;
+ } else {
+ fprintf(stderr,
+ "%s: O_CREAT open(%s %x %o) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->open_flags,
+ file_op->open_mode, errno);
+ exit(EXIT_FAILURE);
+ }
+ } else
+ update_delta_time(&start, io_time);
+ } else {
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ fd = open(files_db_get_filename(db_node),
+ file_op->open_flags);
+ update_delta_time(&start, io_time);
+ if (fd < 0) {
+ if (file_op->open_flags & O_DIRECTORY) {
+ /* O_DIRECTORY open()s should fail */
+ return;
+ } else {
+ fprintf(stderr,
+ "%s: open(%s %x) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->open_flags,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ files_db_close_fd(db_node);
+ files_db_update_fd(db_node, fd);
+ break;
+ case IOSHARK_FSYNC:
+ case IOSHARK_FDATASYNC:
+ if (file_op->file_op == IOSHARK_FSYNC) {
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = fsync(files_db_get_fd(db_node));
+ update_delta_time(&start, io_time);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: fsync(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = fdatasync(files_db_get_fd(db_node));
+ update_delta_time(&start, io_time);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: fdatasync(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ case IOSHARK_CLOSE:
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = close(files_db_get_fd(db_node));
+ update_delta_time(&start, io_time);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: close(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node), errno);
+ exit(EXIT_FAILURE);
+ }
+ files_db_update_fd(db_node, -1);
+ break;
+ default:
+ fprintf(stderr, "%s: unknown FILE_OP %d\n",
+ progname, file_op->file_op);
+ exit(EXIT_FAILURE);
+ break;
+ }
+}
+
+static void
+do_io(struct thread_state_s *state)
+{
+ void *db_node;
+ struct ioshark_header header;
+ struct ioshark_file_operation file_op;
+ u_int64_t prev_delay = 0;
+ int fd;
+ int i;
+ char *buf = NULL;
+ int buflen = 0;
+ struct timeval total_io_time;
+ struct timeval total_delay_time;
+ struct timeval start;
+ u_int64_t op_counts[IOSHARK_MAX_FILE_OP];
+ struct rw_bytes_s rw_bytes;
+
+ rewind(state->fp);
+ if (fread(&header, sizeof(struct ioshark_header), 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, state->filename);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * First open and pre-create all the files. Indexed by fileno.
+ */
+ timerclear(&total_io_time);
+ timerclear(&total_delay_time);
+ memset(&rw_bytes, 0, sizeof(struct rw_bytes_s));
+ memset(op_counts, 0, sizeof(op_counts));
+ fseek(state->fp,
+ sizeof(struct ioshark_header) +
+ header.num_files * sizeof(struct ioshark_file_state),
+ SEEK_SET);
+ /*
+ * Loop over all the IOs, and launch each
+ */
+ for (i = 0 ; i < header.num_io_operations ; i++) {
+ if (fread(&file_op, sizeof(struct ioshark_file_operation),
+ 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ if (do_delay) {
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ usleep(file_op.delta_us - prev_delay);
+ update_delta_time(&start, &total_delay_time);
+ prev_delay = file_op.delta_us;
+ }
+ db_node = files_db_lookup_byfileno(state->db_handle,
+ file_op.fileno);
+ if (db_node == NULL) {
+ fprintf(stderr,
+ "%s Can't lookup fileno %d, fatal error\n",
+ progname, file_op.fileno);
+ exit(EXIT_FAILURE);
+ }
+ if (file_op.file_op != IOSHARK_OPEN &&
+ files_db_get_fd(db_node) == -1) {
+ /*
+ * This is a hack to workaround the fact that we did not
+ * see an open() for this file until now. open() the file
+ * O_RDWR, so that we can perform the IO.
+ */
+ fd = open(files_db_get_filename(db_node), O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "%s: open(%s O_RDWR) error %d\n",
+ progname, files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ files_db_update_fd(db_node, fd);
+ }
+ do_one_io(db_node, &file_op, &total_io_time,
+ op_counts, &rw_bytes, &buf, &buflen);
+ }
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ files_db_fsync_discard_files(state->db_handle);
+ files_db_close_files(state->db_handle);
+ update_delta_time(&start, &total_io_time);
+ update_time(&aggregate_IO_time, &total_io_time);
+ update_time(&aggregate_delay_time, &total_delay_time);
+ update_op_counts(op_counts);
+ update_byte_counts(&aggr_io_rw_bytes, &rw_bytes);
+}
+
+void *
+io_thread(void *unused)
+{
+ struct thread_state_s *state;
+
+ UNUSED_PARAM(unused);
+ srand(gettid());
+ while ((state = get_work()))
+ do_io(state);
+ pthread_exit(NULL);
+ return(NULL);
+}
+
+static void
+do_create(struct thread_state_s *state)
+{
+ struct ioshark_header header;
+
+ if (fread(&header, sizeof(struct ioshark_header), 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, state->filename);
+ exit(EXIT_FAILURE);
+ }
+ state->num_files = header.num_files;
+ state->db_handle = files_db_create_handle();
+ create_files(state);
+}
+
+void *
+create_files_thread(void *unused)
+{
+ struct thread_state_s *state;
+
+ UNUSED_PARAM(unused);
+ while ((state = get_work()))
+ do_create(state);
+ pthread_exit(NULL);
+ return(NULL);
+}
+
+int
+get_start_end(int *start_ix)
+{
+ int i, j, ret_numfiles;
+ u_int64_t free_fs_bytes;
+ char *infile;
+ FILE *fp;
+ struct ioshark_header header;
+ struct ioshark_file_state file_state;
+ struct statfs fsstat;
+ static int fssize_clamp_next_index = 0;
+
+ if (fssize_clamp_next_index == num_input_files)
+ return 0;
+ if (statfs("/data/local/tmp", &fsstat) < 0) {
+ fprintf(stderr, "%s: Can't statfs /data/local/tmp\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ free_fs_bytes = (fsstat.f_bavail * fsstat.f_bsize) * 9 /10;
+ for (i = fssize_clamp_next_index; i < num_input_files; i++) {
+ infile = thread_state[i].filename;
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't open %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ if (fread(&header, sizeof(struct ioshark_header),
+ 1, fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ for (j = 0 ; j < header.num_files ; j++) {
+ if (fread(&file_state, sizeof(struct ioshark_file_state),
+ 1, fp) != 1) {
+ fprintf(stderr, "%s read error tracefile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ if (file_state.size > free_fs_bytes) {
+ fclose(fp);
+ printf("Reducing number of input files from %d down to %d\n",
+ num_input_files,
+ i - fssize_clamp_next_index);
+ goto out;
+ }
+ free_fs_bytes -= file_state.size;
+ }
+ fclose(fp);
+ }
+out:
+ *start_ix = fssize_clamp_next_index;
+ ret_numfiles = i - fssize_clamp_next_index;
+ fssize_clamp_next_index = i;
+ return ret_numfiles;
+}
+
+int
+ioshark_pthread_create(pthread_t *tidp, void *(*start_routine)(void *))
+{
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setstacksize(&attr, (size_t)(1024*1024));
+ return pthread_create(tidp, &attr, start_routine, (void *)NULL);
+}
+
+void
+wait_for_threads(int num_threads)
+{
+ int i;
+
+ for (i = 0; i < num_threads; i++) {
+ pthread_join(tid[i], NULL);
+ tid[i] = 0;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ FILE *fp;
+ struct stat st;
+ char *infile;
+ int num_threads = 0;
+ int num_iterations = 1;
+ int c;
+ int num_files, start_file;
+ struct thread_state_s *state;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "dn:t:")) != EOF) {
+ switch (c) {
+ case 'd':
+ do_delay = 1;
+ break;
+ case 'n':
+ num_iterations = atoi(optarg);
+ break;
+ case 't':
+ num_threads = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (num_threads > MAX_THREADS)
+ usage();
+
+ if (optind == argc)
+ usage();
+
+ for (i = optind; i < argc; i++) {
+ infile = argv[i];
+ if (stat(infile, &st) < 0) {
+ fprintf(stderr, "%s: Can't stat %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ continue;
+ }
+ if (st.st_size == 0) {
+ fprintf(stderr, "%s: Empty file %s\n",
+ progname, infile);
+ continue;
+ }
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't open %s\n",
+ progname, infile);
+ continue;
+ }
+ thread_state[num_input_files].filename = infile;
+ thread_state[num_input_files].fp = fp;
+ num_input_files++;
+ }
+
+ if (num_input_files == 0) {
+ exit(EXIT_SUCCESS);
+ }
+ printf("Total Input Files = %d\n", num_input_files);
+ printf("Num Iterations = %d\n", num_iterations);
+
+ timerclear(&aggregate_file_create_time);
+ timerclear(&aggregate_file_remove_time);
+ timerclear(&aggregate_IO_time);
+
+ /*
+ * We pre-create the files that we need once and then we
+ * loop around N times doing IOs on the pre-created files.
+ *
+ * get_start_end() breaks up the total work here to make sure
+ * that all the files we need to pre-create fit into the
+ * available space in /data/local/tmp (hardcoded for now).
+ *
+ * If it won't fit, then we do several sweeps.
+ */
+ while ((num_files = get_start_end(&start_file))) {
+ /* Create files once */
+ printf("Doing Pre-creation of Files\n");
+ if (num_threads == 0 || num_threads > num_files)
+ num_threads = num_files;
+ (void)system("echo 3 > /proc/sys/vm/drop_caches");
+ init_work(start_file, num_files);
+ for (i = 0; i < num_threads; i++) {
+ if (ioshark_pthread_create(&(tid[i]),
+ create_files_thread)) {
+ fprintf(stderr,
+ "%s: Can't create creator thread %d\n",
+ progname, i);
+ exit(EXIT_FAILURE);
+ }
+ }
+ wait_for_threads(num_threads);
+
+ /* Do the IOs N times */
+ for (i = 0 ; i < num_iterations ; i++) {
+ (void)system("echo 3 > /proc/sys/vm/drop_caches");
+ printf("Starting Test. Iteration %d\n", i);
+ init_work(start_file, num_files);
+ for (c = 0; c < num_threads; c++) {
+ if (ioshark_pthread_create(&(tid[c]),
+ io_thread)) {
+ fprintf(stderr,
+ "%s: Can't create thread %d\n",
+ progname, c);
+ exit(EXIT_FAILURE);
+ }
+ }
+ wait_for_threads(num_threads);
+ }
+ /*
+ * We are done with the N iterations of IO.
+ * Destroy the files we pre-created.
+ */
+ init_work(start_file, num_files);
+ while ((state = get_work())) {
+ struct timeval start;
+
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ files_db_unlink_files(state->db_handle);
+ update_delta_time(&start, &aggregate_file_remove_time);
+ files_db_free_memory(state->db_handle);
+ }
+ }
+ printf("Total Creation time = %lu.%lu (msecs.usecs)\n",
+ get_msecs(&aggregate_file_create_time),
+ get_usecs(&aggregate_file_create_time));
+ printf("Total Remove time = %lu.%lu (msecs.usecs)\n",
+ get_msecs(&aggregate_file_remove_time),
+ get_usecs(&aggregate_file_remove_time));
+ if (do_delay)
+ printf("Total delay time = %lu.%lu (msecs.usecs)\n",
+ get_msecs(&aggregate_delay_time),
+ get_usecs(&aggregate_delay_time));
+ printf("Total IO time = %lu.%lu (msecs.usecs)\n",
+ get_msecs(&aggregate_IO_time),
+ get_usecs(&aggregate_IO_time));
+ print_bytes("Upfront File Creation bytes",
+ &aggr_create_rw_bytes);
+ print_bytes("IO bytes", &aggr_io_rw_bytes);
+ print_op_stats(aggr_op_counts);
+}
diff --git a/ioshark/ioshark_bench.h b/ioshark/ioshark_bench.h
new file mode 100644
index 00000000..6ce9322f
--- /dev/null
+++ b/ioshark/ioshark_bench.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef IOSHARK_MAIN
+const char *IO_op[] = {
+ "LSEEK",
+ "LLSEEK",
+ "PREAD64",
+ "PWRITE64",
+ "READ",
+ "WRITE",
+ "MMAP",
+ "MMAP2",
+ "OPEN",
+ "FSYNC",
+ "FDATASYNC",
+ "CLOSE",
+ "MAPPED_PREAD",
+ "MAPPED_PWRITE",
+ "MAX_FILE_OP"
+};
+#endif
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+
+#define MINBUFLEN (16*1024)
+
+#define FILE_DB_HASHSIZE 8192
+
+struct files_db_s {
+ char *filename;
+ int fileno;
+ size_t size;
+ int fd;
+ int debug_open_flags;
+ struct files_db_s *next;
+};
+
+struct files_db_handle {
+ struct files_db_s *files_db_buckets[FILE_DB_HASHSIZE];
+};
+
+struct IO_operation_s {
+ char *IO_op;
+};
+
+struct rw_bytes_s {
+ u_int64_t bytes_read;
+ u_int64_t bytes_written;
+};
+
+static inline void
+files_db_update_size(void *node, u_int64_t new_size)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->size < new_size)
+ db_node->size = new_size;
+}
+
+static inline void
+files_db_update_filename(void *node, char *filename)
+{
+ ((struct files_db_s *)node)->filename = strdup(filename);
+}
+
+static inline int
+files_db_get_fileno(void *node)
+{
+ return (((struct files_db_s *)node)->fileno);
+}
+
+static inline int
+files_db_get_fd(void *node)
+{
+ return (((struct files_db_s *)node)->fd);
+}
+
+static inline char *
+files_db_get_filename(void *node)
+{
+ return (((struct files_db_s *)node)->filename);
+}
+
+static inline u_int64_t
+get_msecs(struct timeval *tv)
+{
+ return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000));
+}
+
+static inline u_int64_t
+get_usecs(struct timeval *tv)
+{
+ return (tv->tv_usec % 1000);
+}
+
+static inline void
+update_delta_time(struct timeval *start,
+ struct timeval *destination)
+{
+ struct timeval res, finish;
+
+ (void)gettimeofday(&finish, (struct timezone *)NULL);
+ timersub(&finish, start, &res);
+ timeradd(destination, &res, &finish);
+ *destination = finish;
+}
+
+void *files_db_create_handle(void);
+void *files_db_lookup_byfileno(void *handle, int fileno);
+void *files_db_add_byfileno(void *handle, int fileno);
+void files_db_update_fd(void *node, int fd);
+void files_db_unlink_files(void *db_handle);
+void files_db_close_files(void *handle);
+void files_db_close_fd(void *node);
+void files_db_free_memory(void *handle);
+void create_file(char *path, size_t size,
+ struct timeval *total_time,
+ struct rw_bytes_s *rw_bytes);
+char *get_buf(char **buf, int *buflen, int len, int do_fill);
+void files_db_fsync_discard_files(void *handle);
+void print_op_stats(u_int64_t *op_counts);
+void print_bytes(char *desc, struct rw_bytes_s *rw_bytes);
+void ioshark_handle_mmap(void *db_node,
+ struct ioshark_file_operation *file_op,
+ char **bufp, int *buflen, u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes, struct timeval *io_time);
+
+
+
+
+
+
+
+
+
+
diff --git a/ioshark/ioshark_bench_mmap.c b/ioshark/ioshark_bench_mmap.c
new file mode 100644
index 00000000..7ee7c41b
--- /dev/null
+++ b/ioshark/ioshark_bench_mmap.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <linux/fs.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <pthread.h>
+#include "ioshark.h"
+#include "ioshark_bench.h"
+
+/*
+ * The purpose of this code is to convert mmap() calls into
+ * a mix of (semi)-random reads and writes.
+ * PROT_READ => 4KB/8KB/16KB random reads.
+ * PROT_WRITE => adds 4KB random writes.
+ */
+
+extern char *progname;
+
+#define IOSHARK_MAX_MMAP_IOLEN (16*1024)
+
+#define MMAP_ENTS 16
+
+struct mmap_io_ent_tab_s {
+ off_t offset;
+ size_t len;
+};
+
+struct mmap_io_ent_s {
+ int num_entries;
+ struct mmap_io_ent_tab_s table[MMAP_ENTS + 1];
+ size_t resid;
+};
+
+static void
+setup_mmap_io_state(struct mmap_io_ent_s *mio,
+ size_t total_len, off_t offset)
+{
+ int slice;
+
+ memset(mio, 0, sizeof(struct mmap_io_ent_s));
+ mio->resid = total_len;
+ slice = MAX(IOSHARK_MAX_MMAP_IOLEN,
+ total_len / MMAP_ENTS);
+ while (total_len > 0) {
+ assert(mio->num_entries < MMAP_ENTS + 1);
+ mio->table[mio->num_entries].offset = offset;
+ mio->table[mio->num_entries].len =
+ MIN((u_int64_t)total_len, (u_int64_t)slice);
+ total_len -= mio->table[mio->num_entries].len;
+ offset += mio->table[mio->num_entries].len;
+ mio->num_entries++;
+ }
+}
+
+static size_t
+mmap_getnext_off_len(struct mmap_io_ent_s *mio,
+ off_t *offset)
+{
+ int i;
+ int find_rand_index[MMAP_ENTS + 1];
+ int rand_index_len = 0;
+ size_t iolength;
+
+ if (mio->resid == 0)
+ return 0;
+ /* Pick a slot with residual length > 0 at random first */
+ for (i = 0 ; i < MMAP_ENTS + 1 ; i++) {
+ if (mio->table[i].len > 0)
+ find_rand_index[rand_index_len++] = i;
+ }
+ i = find_rand_index[rand() % rand_index_len];
+ /* Randomize iolength 4K-16K */
+ iolength = ((rand() % 4) + 1) * 4096;
+ iolength = MIN(mio->table[i].len, iolength);
+ *offset = mio->table[i].offset;
+ mio->table[i].offset += iolength;
+ mio->table[i].len -= iolength;
+ mio->resid -= iolength;
+ return iolength;
+}
+
+static void
+mmap_do_io(void *db_node, int prot, off_t offset, size_t len,
+ char **bufp, int *buflen, u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes, struct timeval *io_time)
+{
+ struct timeval start;
+ char *p;
+ int ret;
+
+ if (!(prot & IOSHARK_PROT_WRITE)) {
+ /* Only preads */
+ p = get_buf(bufp, buflen, len, 0);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = pread(files_db_get_fd(db_node),
+ p, len, offset);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_read += len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: mapped pread(%s %lu %lu) error fd=%d %s\n",
+ progname, files_db_get_filename(db_node),
+ len, offset, files_db_get_fd(db_node),
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ op_counts[IOSHARK_MAPPED_PREAD]++;
+ } else {
+ /* 50-50 R/W */
+ if ((rand() % 2) == 1) {
+ p = get_buf(bufp, buflen, len, 1);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = pwrite(files_db_get_fd(db_node),
+ p, len, offset);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_written += len;
+ if (ret < 0) {
+#if 0
+ fprintf(stderr,
+ "%s: mapped pwrite failed, file unwriteable ? open_flags=%x\n",
+ progname,
+ fcntl(files_db_get_fd(db_node), F_GETFL));
+ exit(EXIT_FAILURE);
+#endif
+ } else
+ op_counts[IOSHARK_MAPPED_PWRITE]++;
+ } else {
+ p = get_buf(bufp, buflen, len, 0);
+ (void)gettimeofday(&start,
+ (struct timezone *)NULL);
+ ret = pread(files_db_get_fd(db_node),
+ p, len, offset);
+ update_delta_time(&start, io_time);
+ rw_bytes->bytes_read += len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: mapped pread(%s %lu %lu) error fd=%d %s\n",
+ progname, files_db_get_filename(db_node),
+ len,
+ offset, files_db_get_fd(db_node),
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ op_counts[IOSHARK_MAPPED_PREAD]++;
+ }
+ }
+}
+
+void
+ioshark_handle_mmap(void *db_node,
+ struct ioshark_file_operation *file_op,
+ char **bufp, int *buflen, u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes, struct timeval *io_time)
+{
+ off_t offset = file_op->mmap_offset;
+ size_t len = file_op->mmap_len;
+ int prot = file_op->mmap_prot;
+ struct mmap_io_ent_s mio;
+ struct stat statbuf;
+
+ if (fstat(files_db_get_fd(db_node), &statbuf) < 0) {
+ fprintf(stderr,
+ "%s: fstat failure %s\n",
+ __func__, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * The size of the file better accomodate offset + len
+ * Else there is an issue with pre-creation
+ */
+ assert(offset + len <= statbuf.st_size);
+ if (len <= IOSHARK_MAX_MMAP_IOLEN) {
+ mmap_do_io(db_node, prot, offset, len,
+ bufp, buflen, op_counts,
+ rw_bytes, io_time);
+ return;
+ }
+ setup_mmap_io_state(&mio, len, offset);
+ assert(mio.num_entries > 0);
+ while ((len = mmap_getnext_off_len(&mio, &offset))) {
+ assert((offset + len) <=
+ (file_op->mmap_offset + file_op->mmap_len));
+ mmap_do_io(db_node, prot, offset, len, bufp, buflen,
+ op_counts, rw_bytes, io_time);
+ }
+}
diff --git a/ioshark/ioshark_bench_subr.c b/ioshark/ioshark_bench_subr.c
new file mode 100644
index 00000000..ff273cce
--- /dev/null
+++ b/ioshark/ioshark_bench_subr.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <linux/fs.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include "ioshark.h"
+#include "ioshark_bench.h"
+
+extern char *progname;
+
+void *
+files_db_create_handle(void)
+{
+ struct files_db_handle *h;
+ int i;
+
+ h = malloc(sizeof(struct files_db_handle));
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++)
+ h->files_db_buckets[i] = NULL;
+ return h;
+}
+
+void *files_db_lookup_byfileno(void *handle, int fileno)
+{
+ u_int32_t hash;
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+
+ hash = fileno % FILE_DB_HASHSIZE;
+ db_node = h->files_db_buckets[hash];
+ while (db_node != NULL) {
+ if (db_node->fileno == fileno)
+ break;
+ db_node = db_node->next;
+ }
+ return db_node;
+}
+
+void *files_db_add_byfileno(void *handle, int fileno)
+{
+ u_int32_t hash = fileno % FILE_DB_HASHSIZE;
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+
+ db_node = (struct files_db_s *)
+ files_db_lookup_byfileno(handle, fileno);
+ if (db_node == NULL) {
+ db_node = malloc(sizeof(struct files_db_s));
+ db_node->fileno = fileno;
+ db_node->filename = NULL;
+ db_node->size = 0;
+ db_node->fd = -1;
+ db_node->next = h->files_db_buckets[hash];
+ h->files_db_buckets[hash] = db_node;
+ } else {
+ fprintf(stderr,
+ "%s: Node to be added already exists fileno = %d\n\n",
+ __func__, fileno);
+ exit(EXIT_FAILURE);
+ }
+ return db_node;
+}
+
+void
+files_db_fsync_discard_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ int do_close = 0;
+
+ if (db_node->fd == -1) {
+ int fd;
+
+ /*
+ * File was closed, let's open it so we can
+ * fsync and fadvise(DONTNEED) it.
+ */
+ do_close = 1;
+ fd = open(files_db_get_filename(db_node),
+ O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr,
+ "%s: open(%s O_RDWR) error %d\n",
+ progname, db_node->filename,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ db_node->fd = fd;
+ }
+ if (fsync(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot fsync %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ if (posix_fadvise(db_node->fd, 0, 0,
+ POSIX_FADV_DONTNEED) < 0) {
+ fprintf(stderr,
+ "%s: Cannot fadvise(DONTNEED) %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ if (do_close) {
+ close(db_node->fd);
+ db_node->fd = -1;
+ }
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_update_fd(void *node, int fd)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ db_node->fd = fd;
+}
+
+void
+files_db_close_fd(void *node)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->fd != -1)
+ close(db_node->fd);
+ db_node->fd = -1;
+}
+
+void
+files_db_close_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ if ((db_node->fd != -1) && close(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot close %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ db_node->fd = -1;
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_unlink_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ if ((db_node->fd != -1) && close(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot close %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ db_node->fd = -1;
+ if (unlink(db_node->filename) < 0) {
+ fprintf(stderr, "%s: Cannot unlink %s:%s\n",
+ __func__, db_node->filename, strerror(errno));
+ exit(1);
+ }
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_free_memory(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node, *tmp;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ tmp = db_node;
+ db_node = db_node->next;
+ free(tmp->filename);
+ free(tmp);
+ }
+ }
+ free(h);
+}
+
+char *
+get_buf(char **buf, int *buflen, int len, int do_fill)
+{
+ if (len == 0 && *buf == NULL) {
+ /*
+ * If we ever get a zero len
+ * request, start with MINBUFLEN
+ */
+ if (*buf == NULL)
+ len = MINBUFLEN / 2;
+ }
+ if (*buflen < len) {
+ *buflen = MAX(MINBUFLEN, len * 2);
+ if (*buf)
+ free(*buf);
+ *buf = malloc(*buflen);
+ }
+ if (do_fill) {
+ u_int32_t *s;
+ int count;
+
+ s = (u_int32_t *)*buf;
+ count = *buflen / sizeof(u_int32_t);
+ while (count > 0) {
+ *s++ = rand();
+ count--;
+ }
+ }
+ assert(*buf != NULL);
+ return *buf;
+}
+
+void
+create_file(char *path, size_t size, struct timeval *total_time,
+ struct rw_bytes_s *rw_bytes)
+{
+ int fd, n;
+ char *buf = NULL;
+ int buflen = 0;
+ struct timeval start;
+
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ update_delta_time(&start, total_time);
+ if (fd < 0) {
+ fprintf(stderr, "%s Cannot create file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ while (size > 0) {
+ n = MIN(size, MINBUFLEN);
+ buf = get_buf(&buf, &buflen, n, 1);
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ if (write(fd, buf, n) < n) {
+ fprintf(stderr,
+ "%s Cannot write file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ rw_bytes->bytes_written += n;
+ update_delta_time(&start, total_time);
+ size -= n;
+ }
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ if (fsync(fd) < 0) {
+ fprintf(stderr, "%s Cannot fsync file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) < 0) {
+ fprintf(stderr,
+ "%s Cannot fadvise(DONTNEED) file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ close(fd);
+ update_delta_time(&start, total_time);
+}
+
+void
+print_op_stats(u_int64_t *op_counts)
+{
+ int i;
+ extern char *IO_op[];
+
+ printf("IO Operation counts :\n");
+ for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++) {
+ printf("%s: %lu\n",
+ IO_op[i], op_counts[i]);
+ }
+}
+
+void
+print_bytes(char *desc, struct rw_bytes_s *rw_bytes)
+{
+ printf("%s: Reads = %dMB, Writes = %dMB\n",
+ desc,
+ (int)(rw_bytes->bytes_read / (1024 * 1024)),
+ (int)(rw_bytes->bytes_written / (1024 * 1024)));
+}
+