OSDN Git Service

ioshark: A Repeatable Application Workload Based Storage Benchmark.
authorMohan Srinivasan <srmohan@google.com>
Thu, 15 Dec 2016 21:28:50 +0000 (13:28 -0800)
committerMohan Srinivasan <srmohan@google.com>
Fri, 16 Dec 2016 20:42:30 +0000 (12:42 -0800)
ioshark is a repeatable application storage workload benchmark. ioshark
takes as input strace files, compiles them into bytecodes that you can
then feed into the benchmark. This allows us to collect straces for a
chosen application workload (by stracing zygote/zygote64), and use
these to do A-B comparisons of storage features (anything including the
filesystem and below, including new hardware).

Test: Extra tests for ioshark aren't needed, ioshark reports detailed
stats at the end of the run, which tells us whether the ioshark run
was successful or not. In addition, we can strace ioshark_bench as it
runs to make sure it is launching IOs as we would expect it to.

Change-Id: Ieb0ecf5e32695ea95b361710aa31613f6ce63b97
Signed-off-by: Mohan Srinivasan <srmohan@google.com>
ioshark/Android.mk [new file with mode: 0644]
ioshark/README [new file with mode: 0644]
ioshark/collect-straces.sh [new file with mode: 0755]
ioshark/compile_ioshark.c [new file with mode: 0644]
ioshark/compile_ioshark.h [new file with mode: 0644]
ioshark/compile_ioshark_subr.c [new file with mode: 0644]
ioshark/ioshark.h [new file with mode: 0644]
ioshark/ioshark_bench.c [new file with mode: 0644]
ioshark/ioshark_bench.h [new file with mode: 0644]
ioshark/ioshark_bench_mmap.c [new file with mode: 0644]
ioshark/ioshark_bench_subr.c [new file with mode: 0644]

diff --git a/ioshark/Android.mk b/ioshark/Android.mk
new file mode 100644 (file)
index 0000000..a2e4816
--- /dev/null
@@ -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 (file)
index 0000000..3afbe76
--- /dev/null
@@ -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 (executable)
index 0000000..4577e73
--- /dev/null
@@ -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 (file)
index 0000000..609d2bc
--- /dev/null
@@ -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 (file)
index 0000000..13095bf
--- /dev/null
@@ -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 (file)
index 0000000..e2b6b89
--- /dev/null
@@ -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 (file)
index 0000000..f0a715f
--- /dev/null
@@ -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 (file)
index 0000000..9bbfcae
--- /dev/null
@@ -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 (file)
index 0000000..6ce9322
--- /dev/null
@@ -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 (file)
index 0000000..7ee7c41
--- /dev/null
@@ -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 (file)
index 0000000..ff273cc
--- /dev/null
@@ -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)));
+}
+