summaryrefslogtreecommitdiff
path: root/ioshark
diff options
context:
space:
mode:
authorMohan Srinivasan <srmohan@google.com>2017-01-17 12:13:07 -0800
committerMohan Srinivasan <srmohan@google.com>2017-01-18 12:06:24 -0800
commita5dc6d6fcab8dfe719c437e8a9111ba962df1c15 (patch)
tree7d572bf964d07251f5bd1b2bd6d8777ac01f46ac /ioshark
parent1a1fc822ed14eda41d306d9d3b375cf5dd63a58d (diff)
downloadextras-a5dc6d6fcab8dfe719c437e8a9111ba962df1c15.tar.gz
ioshark: A Repeatable Application Workload Based Storage Benchmark.
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: I70674ff8daac229b9f8e5d37780f15c7a31a7ca0 Signed-off-by: Mohan Srinivasan <srmohan@google.com>
Diffstat (limited to 'ioshark')
-rw-r--r--ioshark/Android.mk32
-rw-r--r--ioshark/README27
-rwxr-xr-xioshark/collect-straces.sh93
-rw-r--r--ioshark/compile_ioshark.c577
-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.c739
-rw-r--r--ioshark/ioshark_bench.h153
-rw-r--r--ioshark/ioshark_bench_mmap.c202
-rw-r--r--ioshark/ioshark_bench_subr.c493
-rw-r--r--ioshark/wl.tarbin0 -> 1566720 bytes
12 files changed, 2604 insertions, 0 deletions
diff --git a/ioshark/Android.mk b/ioshark/Android.mk
new file mode 100644
index 00000000..2152c06d
--- /dev/null
+++ b/ioshark/Android.mk
@@ -0,0 +1,32 @@
+# 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_32_BIT_ONLY = true
+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_32_BIT_ONLY = true
+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..ee2f426b
--- /dev/null
+++ b/ioshark/README
@@ -0,0 +1,27 @@
+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.
+-v : verbose. Chatty mode.
diff --git a/ioshark/collect-straces.sh b/ioshark/collect-straces.sh
new file mode 100755
index 00000000..1071a33e
--- /dev/null
+++ b/ioshark/collect-straces.sh
@@ -0,0 +1,93 @@
+# 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
+adb shell 'ps' | 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..683b5975
--- /dev/null
+++ b/ioshark/compile_ioshark.c
@@ -0,0 +1,577 @@
+/*
+ * 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 <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, "%ju", 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, "%jx", 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, "%ju", 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, "%ju", 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, "%ju", len);
+ sscanf(s_offset + 2, "%ju", 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,
+ (u_int64_t *)&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, (u_int64_t *)&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,
+ (u_int64_t *)&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..5b19d2a9
--- /dev/null
+++ b/ioshark/ioshark_bench.c
@@ -0,0 +1,739 @@
+/*
+ * 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 <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"
+
+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;
+int verbose = 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 debug_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;
+
+/*
+ * Locking needed here because aggregate_delay_time is updated
+ * from multiple threads concurrently.
+ */
+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)
+{
+ int i;
+ struct ioshark_file_state file_state;
+ char path[512];
+ void *db_node;
+ struct rw_bytes_s rw_bytes;
+
+ 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,
+ &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);
+}
+
+static void
+do_one_io(void *db_node,
+ struct ioshark_file_operation *file_op,
+ u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes,
+ char **bufp, int *buflen)
+{
+ 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:
+ ret = lseek(files_db_get_fd(db_node),
+ file_op->lseek_offset,
+ file_op->lseek_action);
+ 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);
+ ret = pread(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ rw_bytes->bytes_read += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pread(%s %zu %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);
+ ret = pwrite(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ rw_bytes->bytes_written += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pwrite(%s %zu %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);
+ ret = read(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ rw_bytes->bytes_read += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: read(%s %zu) 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);
+ ret = write(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ rw_bytes->bytes_written += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: write(%s %zu) 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);
+ break;
+ case IOSHARK_OPEN:
+ if (file_op->open_flags & O_CREAT) {
+ 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) {
+ 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 {
+ fd = open(files_db_get_filename(db_node),
+ file_op->open_flags);
+ 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) {
+ ret = fsync(files_db_get_fd(db_node));
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: fsync(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ ret = fdatasync(files_db_get_fd(db_node));
+ 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:
+ ret = close(files_db_get_fd(db_node));
+ 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_delay_time;
+ 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_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) {
+ struct timeval start;
+
+ (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,
+ op_counts, &rw_bytes, &buf, &buflen);
+ }
+ files_db_fsync_discard_files(state->db_handle);
+ files_db_close_files(state->db_handle);
+ 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 __attribute__((unused)))
+{
+ struct thread_state_s *state;
+
+ 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 __attribute__((unused)))
+{
+ struct thread_state_s *state;
+
+ 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;
+ static int chunk = 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);
+ goto out;
+ }
+ free_fs_bytes -= file_state.size;
+ }
+ fclose(fp);
+ }
+out:
+ if (verbose) {
+ if (chunk > 0 || i < num_input_files) {
+ printf("Breaking up input files, Chunk %d: %d to %d\n",
+ chunk++, fssize_clamp_next_index, i - 1);
+ } else {
+ printf("Entire Dataset fits start = %d to %d, free_bytes = %ju\n",
+ fssize_clamp_next_index,
+ i - fssize_clamp_next_index,
+ free_fs_bytes);
+ }
+ }
+ *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:v")) != EOF) {
+ switch (c) {
+ case 'd':
+ do_delay = 1;
+ break;
+ case 'n':
+ num_iterations = atoi(optarg);
+ break;
+ case 't':
+ num_threads = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ 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);
+ }
+ if (verbose) {
+ 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);
+
+ capture_util_state_before();
+
+ /*
+ * 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))) {
+ struct timeval time_for_pass;
+
+ /* 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);
+ (void)gettimeofday(&time_for_pass,
+ (struct timezone *)NULL);
+ 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);
+ update_delta_time(&time_for_pass, &aggregate_file_create_time);
+
+ /* Do the IOs N times */
+ for (i = 0 ; i < num_iterations ; i++) {
+ (void)system("echo 3 > /proc/sys/vm/drop_caches");
+ if (num_iterations > 1)
+ printf("Starting Test. Iteration %d...\n",
+ i);
+ else
+ printf("Starting Test...\n");
+ init_work(start_file, num_files);
+ (void)gettimeofday(&time_for_pass,
+ (struct timezone *)NULL);
+ 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);
+ update_delta_time(&time_for_pass,
+ &aggregate_IO_time);
+ }
+
+ /*
+ * 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 = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_file_create_time),
+ get_usecs(&aggregate_file_create_time));
+ printf("Total Remove time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_file_remove_time),
+ get_usecs(&aggregate_file_remove_time));
+ if (do_delay)
+ printf("Total delay time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_delay_time),
+ get_usecs(&aggregate_delay_time));
+ printf("Total Test (IO) time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_IO_time),
+ get_usecs(&aggregate_IO_time));
+ if (verbose)
+ print_bytes("Upfront File Creation bytes",
+ &aggr_create_rw_bytes);
+ print_bytes("Total Test (IO) bytes", &aggr_io_rw_bytes);
+ if (verbose)
+ print_op_stats(aggr_op_counts);
+ report_cpu_disk_util();
+}
diff --git a/ioshark/ioshark_bench.h b/ioshark/ioshark_bench.h
new file mode 100644
index 00000000..95e6a317
--- /dev/null
+++ b/ioshark/ioshark_bench.h
@@ -0,0 +1,153 @@
+/*
+ * 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 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);
+void capture_util_state_before(void);
+void report_cpu_disk_util(void);
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ioshark/ioshark_bench_mmap.c b/ioshark/ioshark_bench_mmap.c
new file mode 100644
index 00000000..5eec23c5
--- /dev/null
+++ b/ioshark/ioshark_bench_mmap.c
@@ -0,0 +1,202 @@
+/*
+ * 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 <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)
+{
+ char *p;
+ int ret;
+
+ if (!(prot & IOSHARK_PROT_WRITE)) {
+ /* Only preads */
+ p = get_buf(bufp, buflen, len, 0);
+ ret = pread(files_db_get_fd(db_node),
+ p, len, offset);
+ rw_bytes->bytes_read += len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: mapped pread(%s %zu %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);
+ ret = pwrite(files_db_get_fd(db_node),
+ p, len, offset);
+ 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);
+ ret = pread(files_db_get_fd(db_node),
+ p, len, offset);
+ rw_bytes->bytes_read += len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: mapped pread(%s %zu %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)
+{
+ 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);
+ 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);
+ }
+}
diff --git a/ioshark/ioshark_bench_subr.c b/ioshark/ioshark_bench_subr.c
new file mode 100644
index 00000000..4083e053
--- /dev/null
+++ b/ioshark/ioshark_bench_subr.c
@@ -0,0 +1,493 @@
+/*
+ * 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 <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 __attribute__((unused)))
+{
+ 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 rw_bytes_s *rw_bytes)
+{
+ int fd, n;
+ char *buf = NULL;
+ int buflen = 0;
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ 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);
+ 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;
+ size -= n;
+ }
+ 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);
+}
+
+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: %ju\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)));
+}
+
+struct cpu_disk_util_stats {
+ /* CPU util */
+ u_int64_t user_cpu_ticks;
+ u_int64_t nice_cpu_ticks;
+ u_int64_t system_cpu_ticks;
+ u_int64_t idle_cpu_ticks;
+ u_int64_t iowait_cpu_ticks;
+ u_int64_t hardirq_cpu_ticks;
+ u_int64_t softirq_cpu_ticks;
+ /* disk util */
+ unsigned long long uptime;
+ unsigned int tot_ticks;
+ unsigned long rd_ios;
+ unsigned long wr_ios;
+ unsigned long rd_sec;
+ unsigned long wr_sec;
+};
+
+static struct cpu_disk_util_stats before;
+static struct cpu_disk_util_stats after;
+
+#define BUFSIZE 8192
+
+static int hz;
+
+static void
+get_HZ(void)
+{
+ if ((hz = sysconf(_SC_CLK_TCK)) == -1)
+ exit(1);
+}
+
+#if 0
+static int num_cores;
+
+static void
+get_cores(void)
+{
+ if ((num_cores = sysconf(_SC_NPROCESSORS_ONLN)) == -1)
+ exit(1);
+}
+#endif
+
+static void
+read_disk_util_state(struct cpu_disk_util_stats *state)
+{
+ FILE *fp;
+ char line[BUFSIZE], dev_name[BUFSIZE];
+ unsigned int major, minor;
+ unsigned int ios_pgr;
+ unsigned int rq_ticks;
+ unsigned int wr_ticks;
+ unsigned long rd_ticks;
+ unsigned long rd_merges;
+ unsigned long wr_merges;
+ unsigned long up_sec, up_cent;
+
+ /* Read and parse /proc/uptime */
+ fp = fopen("/proc/uptime", "r");
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ fprintf(stderr, "%s: Cannot read /proc/uptime\n",
+ progname);
+ exit(1);
+ }
+ fclose(fp);
+ sscanf(line, "%lu.%lu", &up_sec, &up_cent);
+ state->uptime = (unsigned long long) up_sec * hz +
+ (unsigned long long) up_cent * hz / 100;
+ /* Read and parse /proc/diskstats */
+ fp = fopen("/proc/diskstats", "r");
+ while (fgets(line, sizeof(line), fp)) {
+ sscanf(line,
+ "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u",
+ &major, &minor, dev_name,
+ &state->rd_ios, &rd_merges, &state->rd_sec,
+ &rd_ticks, &state->wr_ios, &wr_merges,
+ &state->wr_sec, &wr_ticks,
+ &ios_pgr, &state->tot_ticks, &rq_ticks);
+ if (strcmp(dev_name, "sda") == 0) {
+ /*
+ * tot_ticks is "number of milliseconds spent
+ * doing I/Os". Look at Documentation/iostats.txt.
+ * Or at genhd.c:diskstats_show(), which calls
+ * jiffies_to_msecs() on this field before printing
+ * it. Convert this to hz, so we can do all our math
+ * in ticks.
+ */
+ state->tot_ticks /= 1000; /* to seconds */
+ state->tot_ticks *= hz; /* to hz */
+ fclose(fp);
+ return;
+ }
+ }
+ fprintf(stderr, "%s: Did not find device sda in /proc/diskstats\n",
+ progname);
+ exit(1);
+}
+
+static void
+read_cpu_util_state(struct cpu_disk_util_stats *state)
+{
+ FILE *fp;
+ char line[BUFSIZE], cpu[BUFSIZE];
+
+ /* Read and parse /proc/stat */
+ fp = fopen("/proc/stat", "r");
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ fprintf(stderr, "%s: Cannot read /proc/stat\n",
+ progname);
+ exit(1);
+ }
+ fclose(fp);
+ sscanf(line, "%s %ju %ju %ju %ju %ju %ju %ju",
+ cpu,
+ &state->user_cpu_ticks,
+ &state->nice_cpu_ticks,
+ &state->system_cpu_ticks,
+ &state->idle_cpu_ticks,
+ &state->iowait_cpu_ticks,
+ &state->hardirq_cpu_ticks,
+ &state->softirq_cpu_ticks);
+}
+
+void
+capture_util_state_before(void)
+{
+ get_HZ();
+ read_disk_util_state(&before);
+ read_cpu_util_state(&before);
+}
+
+extern int verbose;
+
+void
+report_cpu_disk_util(void)
+{
+ double disk_util, cpu_util;
+ u_int64_t tot1, tot2, delta1, delta2;
+
+ read_disk_util_state(&after);
+ read_cpu_util_state(&after);
+ /* CPU Util */
+ tot2 = after.user_cpu_ticks + after.nice_cpu_ticks +
+ after.system_cpu_ticks + after.hardirq_cpu_ticks +
+ after.softirq_cpu_ticks;
+ tot1 = before.user_cpu_ticks + before.nice_cpu_ticks +
+ before.system_cpu_ticks + before.hardirq_cpu_ticks +
+ before.softirq_cpu_ticks;
+ delta1 = tot2 - tot1;
+ tot2 += after.iowait_cpu_ticks + after.idle_cpu_ticks;
+ tot1 += before.iowait_cpu_ticks + before.idle_cpu_ticks;
+ delta2 = tot2 - tot1;
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("CPU util = %.2f%%\n", cpu_util);
+ /* Next compute system (incl irq/softirq) and user cpu util */
+ delta1 = (after.user_cpu_ticks + after.nice_cpu_ticks) -
+ (before.user_cpu_ticks + before.nice_cpu_ticks);
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("User CPU util = %.2f%%\n", cpu_util);
+ delta1 = (after.system_cpu_ticks + after.hardirq_cpu_ticks +
+ after.softirq_cpu_ticks) -
+ (before.system_cpu_ticks + before.hardirq_cpu_ticks +
+ before.softirq_cpu_ticks);
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("System CPU util = %.2f%%\n", cpu_util);
+ /* Disk Util */
+ disk_util = (after.tot_ticks - before.tot_ticks) * 100.0 /
+ (after.uptime - before.uptime);
+ if (verbose) {
+ printf("Reads : nr_ios %lu, MB read %lu\n",
+ (after.rd_ios - before.rd_ios),
+ (after.rd_sec - before.rd_sec) / 2048);
+ printf("Writes : nr_ios %lu, MB written %lu\n",
+ (after.wr_ios - before.wr_ios),
+ (after.wr_sec - before.wr_sec) / 2048);
+ }
+ printf("Disk util = %.2f%%\n", disk_util);
+}
diff --git a/ioshark/wl.tar b/ioshark/wl.tar
new file mode 100644
index 00000000..af7d964e
--- /dev/null
+++ b/ioshark/wl.tar
Binary files differ