OSDN Git Service

Merge "simpleperf: Support Ctrl-C in app_profiler.py." am: 7941d1917b
authorYabin Cui <yabinc@google.com>
Thu, 22 Jun 2017 19:14:52 +0000 (19:14 +0000)
committerandroid-build-merger <android-build-merger@google.com>
Thu, 22 Jun 2017 19:14:52 +0000 (19:14 +0000)
am: c1a5f290f6

Change-Id: I2328a2844cccfe1db5db44a824374b44122d888f

42 files changed:
ANRdaemon/ANRdaemon.cpp
cppreopts/cppreopts.sh
ext4_utils/allocate.c
ext4_utils/ext4_crypt.cpp
ext4_utils/ext4_crypt_init_extensions.cpp
ext4_utils/include/ext4_utils/ext4_crypt.h
ext4_utils/mkuserimg_mke2fs.sh
ioshark/Android.mk [new file with mode: 0644]
ioshark/README [new file with mode: 0644]
ioshark/collect-straces-ftraces.sh [new file with mode: 0644]
ioshark/collect-straces.sh [new file with mode: 0755]
ioshark/compile-only.sh [new file with mode: 0644]
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/dump_ioshark_filenames.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]
ioshark/monkey-strace+fstrace.tgz [new file with mode: 0644]
ioshark/monkeystracebig.tgz [new file with mode: 0644]
ioshark/monkeytracebig.tar.gz [new file with mode: 0644]
ioshark/wl.tar [new file with mode: 0644]
memcpy-perf/memcpy-perf.cpp
preopt2cachename/preopt2cachename.cpp
puncture_fs/puncture_fs.c
simpleperf/cpu_hotplug_test.cpp
tests/bootloader/Android.mk [new file with mode: 0644]
tests/icachetest/Android.mk
tests/icachetest/Profiler.cpp [new file with mode: 0644]
tests/icachetest/Profiler.h [new file with mode: 0644]
tests/icachetest/icache.S
tests/icachetest/icache2.S [deleted file]
tests/icachetest/icache_main.c [deleted file]
tests/icachetest/icache_main.cpp [new file with mode: 0644]
tests/kernel.config/Android.mk
tests/kernel.config/AndroidTest.xml
tests/sdcard/sdcard_perf_test.cpp
tests/workloads/pwrsummary.sh
tests/workloads/pwrtest.sh

index 35f2ecb..c33414c 100644 (file)
@@ -134,7 +134,7 @@ static void get_cpu_stat(cpu_stat_t *cpu) {
 
     if ((fp = fopen("/proc/stat", "r")) == NULL) {
         err = true;
-        sprintf(err_msg, "can't read from /proc/stat with errno %d", errno);
+        snprintf(err_msg, sizeof(err_msg), "can't read from /proc/stat with errno %d", errno);
     } else {
         if (fscanf(fp, params, &cpu->utime, &cpu->ntime,
                 &cpu->stime, &cpu->itime, &cpu->iowtime, &cpu->irqtime,
@@ -144,6 +144,7 @@ static void get_cpu_stat(cpu_stat_t *cpu) {
              * is_heavy_loaded() will return false.
              */
             ALOGE("Error in getting cpu status. Skipping this check.");
+            fclose(fp);
             return;
         }
 
@@ -157,7 +158,7 @@ static void get_cpu_stat(cpu_stat_t *cpu) {
 /*
  * Calculate cpu usage in the past interval.
  * If tracing is on, increase the idle threshold by 1.00% so that we do not
- * turn on and off tracing frequently whe the cpu load is right close to
+ * turn on and off tracing frequently when the cpu load is right close to
  * threshold.
  */
 static bool is_heavy_load(void) {
@@ -192,7 +193,7 @@ static int dfs_enable(bool enable, const char* path) {
     int fd = open(path, O_WRONLY);
     if (fd == -1) {
         err = true;
-        sprintf(err_msg, "Can't open %s. Error: %d", path, errno);
+        snprintf(err_msg, sizeof(err_msg), "Can't open %s. Error: %d", path, errno);
         return -1;
     }
     const char* control = (enable?"1":"0");
@@ -205,7 +206,7 @@ static int dfs_enable(bool enable, const char* path) {
         }
 
         err = true;
-        sprintf(err_msg, "Error %d in writing to %s.", errno, path);
+        snprintf(err_msg, sizeof(err_msg), "Error %d in writing to %s.", errno, path);
     }
     close(fd);
     return (err?-1:0);
@@ -216,16 +217,16 @@ static int dfs_enable(bool enable, const char* path) {
  */
 static void dfs_set_property(uint64_t mtag, const char* mapp, bool enable) {
     char buf[64];
-    snprintf(buf, 64, "%#" PRIx64, mtag);
+    snprintf(buf, sizeof(buf), "%#" PRIx64, mtag);
     if (property_set(dfs_tags_property, buf) < 0) {
         err = true;
-        sprintf(err_msg, "Failed to set debug tags system properties.");
+        snprintf(err_msg, sizeof(err_msg), "Failed to set debug tags system properties.");
     }
 
     if (strlen(mapp) > 0
             && property_set(dfs_apps_property, mapp) < 0) {
         err = true;
-        sprintf(err_msg, "Failed to set debug applications.");
+        snprintf(err_msg, sizeof(err_msg), "Failed to set debug applications.");
     }
 
     if (log_sched) {
@@ -403,13 +404,13 @@ static int set_tracing_buffer_size(void) {
     int fd = open(dfs_buffer_size_path, O_WRONLY);
     if (fd == -1) {
         err = true;
-        sprintf(err_msg, "Can't open atrace buffer size file under /d/tracing.");
+        snprintf(err_msg, sizeof(err_msg), "Can't open atrace buffer size file under /d/tracing.");
         return -1;
     }
     ssize_t len = strlen(buf_size_kb);
     if (write(fd, buf_size_kb, len) != len) {
         err = true;
-        sprintf(err_msg, "Error in writing to atrace buffer size file.");
+        snprintf(err_msg, sizeof(err_msg), "Error in writing to atrace buffer size file.");
     }
     close(fd);
     return (err?-1:0);
index 8798206..9f21ac7 100644 (file)
@@ -19,20 +19,20 @@ umask 022
 
 # Helper function to copy files
 function do_copy() {
-  odex_file=$1
+  source_file=$1
   dest_name=$2
   # Move to a temporary file so we can do a rename and have the preopted file
   # appear atomically in the filesystem.
   temp_dest_name=${dest_name}.tmp
-  if ! cp ${odex_file} ${temp_dest_name} ; then
-    log -p w -t cppreopts "Unable to copy odex file ${odex_file} to ${temp_dest_name}!"
+  if ! cp ${source_file} ${temp_dest_name} ; then
+    log -p w -t cppreopts "Unable to copy file ${source_file} to ${temp_dest_name}!"
   else
-    log -p i -t cppreopts "Copied odex file from ${odex_file} to ${temp_dest_name}"
+    log -p i -t cppreopts "Copied file from ${source_file} to ${temp_dest_name}"
     sync
     if ! mv ${temp_dest_name} ${dest_name} ; then
-      log -p w -t cppreopts "Unable to rename temporary odex file from ${temp_dest_name} to ${dest_name}"
+      log -p w -t cppreopts "Unable to rename temporary file from ${temp_dest_name} to ${dest_name}"
     else
-      log -p i -t cppreopts "Renamed temporary odex file from ${temp_dest_name} to ${dest_name}"
+      log -p i -t cppreopts "Renamed temporary file from ${temp_dest_name} to ${dest_name}"
     fi
   fi
 }
@@ -42,23 +42,23 @@ if [ $# -eq 1 ]; then
   mountpoint=$1
 
   if ! test -f ${mountpoint}/system-other-odex-marker ; then
-    log -p i -t cppreopts "system_other partition does not appear have been built to contain preopted files."
+    log -p i -t cppreopts "system_other partition does not appear to have been built to contain preopted files."
     exit 1
   fi
 
   log -p i -t cppreopts "cppreopts from ${mountpoint}"
-  # For each odex file do the copy task
+  # For each odex and vdex file do the copy task
   # NOTE: this implementation will break in any path with spaces to favor
   # background copy tasks
-  for odex_file in $(find ${mountpoint} -type f -name "*.odex"); do
-    real_odex_name=${odex_file/${mountpoint}/\/system}
-    dest_name=$(preopt2cachename ${real_odex_name})
+  for file in $(find ${mountpoint} -type f -name "*.odex" -o -type f -name "*.vdex"); do
+    real_name=${file/${mountpoint}/\/system}
+    dest_name=$(preopt2cachename ${real_name})
     if ! test $? -eq 0 ; then
-      log -p i -t cppreopts "Unable to figure out destination for ${odex_file}"
+      log -p i -t cppreopts "Unable to figure out destination for ${file}"
       continue
     fi
     # Copy files in background to speed things up
-    do_copy ${odex_file} ${dest_name} &
+    do_copy ${file} ${dest_name} &
   done
   # Wait for jobs to finish
   wait
index 00f2203..28fc8e5 100644 (file)
@@ -234,6 +234,18 @@ static void free_blocks(struct block_group_info *bg, u32 block, u32 num_blocks)
        for (i = 0; i < num_blocks; i++, block--)
                bg->block_bitmap[block / 8] &= ~(1 << (block % 8));
        bg->free_blocks += num_blocks;
+       for (i = bg->chunk_count; i > 0 ;) {
+               --i;
+               if (bg->chunks[i].len >= num_blocks && bg->chunks[i].block <= block) {
+                       if (bg->chunks[i].block == block) {
+                               bg->chunks[i].block += num_blocks;
+                               bg->chunks[i].len -= num_blocks;
+                       } else if (bg->chunks[i].block + bg->chunks[i].len - 1 == block + num_blocks) {
+                               bg->chunks[i].len -= num_blocks;
+                       }
+                       break;
+               }
+       }
 }
 
 /* Reduces an existing allocation by len blocks by return the last blocks
index d594a48..de85f7e 100644 (file)
 #define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
 
 struct ext4_encryption_policy {
-    char version;
-    char contents_encryption_mode;
-    char filenames_encryption_mode;
-    char flags;
-    char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+    uint8_t version;
+    uint8_t contents_encryption_mode;
+    uint8_t filenames_encryption_mode;
+    uint8_t flags;
+    uint8_t master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
 } __attribute__((__packed__));
 
 #define EXT4_ENCRYPTION_MODE_AES_256_XTS    1
 #define EXT4_ENCRYPTION_MODE_AES_256_CTS    4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH    126
 #define EXT4_ENCRYPTION_MODE_PRIVATE        127
 
+#define EXT4_POLICY_FLAGS_PAD_4         0x00
+#define EXT4_POLICY_FLAGS_PAD_8         0x01
+#define EXT4_POLICY_FLAGS_PAD_16        0x02
+#define EXT4_POLICY_FLAGS_PAD_32        0x03
+#define EXT4_POLICY_FLAGS_PAD_MASK      0x03
+#define EXT4_POLICY_FLAGS_VALID         0x03
+
 // ext4enc:TODO Get value from somewhere sensible
 #define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
 #define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
@@ -100,8 +108,25 @@ static bool is_dir_empty(const char *dirname, bool *is_empty)
     return true;
 }
 
+static uint8_t e4crypt_get_policy_flags(int filenames_encryption_mode) {
+
+    // With HEH, pad filenames with zeroes to the next 16-byte boundary.  This
+    // is not required, but it's more secure (helps hide the length of
+    // filenames), makes the inputs evenly divisible into blocks which is more
+    // efficient for encryption and decryption, and we had the opportunity to
+    // make a breaking change when introducing a new mode anyway.
+    if (filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH) {
+        return EXT4_POLICY_FLAGS_PAD_16;
+    }
+
+    // Default flags (4-byte padding) for CTS
+    return EXT4_POLICY_FLAGS_PAD_4;
+}
+
 static bool e4crypt_policy_set(const char *directory, const char *policy,
-                               size_t policy_length, int contents_encryption_mode) {
+                               size_t policy_length,
+                               int contents_encryption_mode,
+                               int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
@@ -115,8 +140,8 @@ static bool e4crypt_policy_set(const char *directory, const char *policy,
     ext4_encryption_policy eep;
     eep.version = 0;
     eep.contents_encryption_mode = contents_encryption_mode;
-    eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
-    eep.flags = 0;
+    eep.filenames_encryption_mode = filenames_encryption_mode;
+    eep.flags = e4crypt_get_policy_flags(filenames_encryption_mode);
     memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
     if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
         PLOG(ERROR) << "Failed to set encryption policy for " << directory;
@@ -132,7 +157,9 @@ static bool e4crypt_policy_set(const char *directory, const char *policy,
 }
 
 static bool e4crypt_policy_get(const char *directory, char *policy,
-                               size_t policy_length, int contents_encryption_mode) {
+                               size_t policy_length,
+                               int contents_encryption_mode,
+                               int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
@@ -155,8 +182,9 @@ static bool e4crypt_policy_get(const char *directory, char *policy,
 
     if ((eep.version != 0)
             || (eep.contents_encryption_mode != contents_encryption_mode)
-            || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
-            || (eep.flags != 0)) {
+            || (eep.filenames_encryption_mode != filenames_encryption_mode)
+            || (eep.flags !=
+                e4crypt_get_policy_flags(filenames_encryption_mode))) {
         LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
         return false;
     }
@@ -166,14 +194,17 @@ static bool e4crypt_policy_get(const char *directory, char *policy,
 }
 
 static bool e4crypt_policy_check(const char *directory, const char *policy,
-                                 size_t policy_length, int contents_encryption_mode) {
+                                 size_t policy_length,
+                                 int contents_encryption_mode,
+                                 int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
     }
     char existing_policy[EXT4_KEY_DESCRIPTOR_SIZE];
     if (!e4crypt_policy_get(directory, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE,
-                            contents_encryption_mode)) return false;
+                            contents_encryption_mode,
+                            filenames_encryption_mode)) return false;
     char existing_policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
 
     policy_to_hex(existing_policy, existing_policy_hex);
@@ -191,14 +222,30 @@ static bool e4crypt_policy_check(const char *directory, const char *policy,
 }
 
 int e4crypt_policy_ensure(const char *directory, const char *policy,
-                          size_t policy_length, const char* contents_encryption_mode) {
-    int mode = 0;
-    if (!strcmp(contents_encryption_mode, "software")) {
-        mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+                          size_t policy_length,
+                          const char *contents_encryption_mode,
+                          const char *filenames_encryption_mode) {
+    int contents_mode = 0;
+    int filenames_mode = 0;
+
+    if (!strcmp(contents_encryption_mode, "software") ||
+        !strcmp(contents_encryption_mode, "aes-256-xts")) {
+        contents_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
     } else if (!strcmp(contents_encryption_mode, "ice")) {
-        mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+        contents_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+    } else {
+        LOG(ERROR) << "Invalid file contents encryption mode: "
+                   << contents_encryption_mode;
+        return -1;
+    }
+
+    if (!strcmp(filenames_encryption_mode, "aes-256-cts")) {
+        filenames_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+    } else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) {
+        filenames_mode = EXT4_ENCRYPTION_MODE_AES_256_HEH;
     } else {
-        LOG(ERROR) << "Invalid encryption mode";
+        LOG(ERROR) << "Invalid file names encryption mode: "
+                   << filenames_encryption_mode;
         return -1;
     }
 
@@ -206,10 +253,10 @@ int e4crypt_policy_ensure(const char *directory, const char *policy,
     if (!is_dir_empty(directory, &is_empty)) return -1;
     if (is_empty) {
         if (!e4crypt_policy_set(directory, policy, policy_length,
-                                mode)) return -1;
+                                contents_mode, filenames_mode)) return -1;
     } else {
         if (!e4crypt_policy_check(directory, policy, policy_length,
-                                  mode)) return -1;
+                                  contents_mode, filenames_mode)) return -1;
     }
     return 0;
 }
index 04d7119..2bf8801 100644 (file)
@@ -28,6 +28,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <keyutils.h>
@@ -91,14 +92,23 @@ int e4crypt_set_directory_policy(const char* dir)
     }
 
     auto type_filename = std::string("/data") + e4crypt_key_mode;
-    std::string contents_encryption_mode;
-    if (!android::base::ReadFileToString(type_filename, &contents_encryption_mode)) {
+    std::string modestring;
+    if (!android::base::ReadFileToString(type_filename, &modestring)) {
         LOG(ERROR) << "Cannot read mode";
     }
 
+    std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+    if (modes.size() < 1 || modes.size() > 2) {
+        LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+        return -1;
+    }
+
     LOG(INFO) << "Setting policy on " << dir;
     int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.length(),
-                                       contents_encryption_mode.c_str());
+                                       modes[0].c_str(),
+                                       modes.size() >= 2 ?
+                                            modes[1].c_str() : "aes-256-cts");
     if (result) {
         LOG(ERROR) << android::base::StringPrintf(
             "Setting %02x%02x%02x%02x policy on %s failed!",
index 2be0bec..d410ccf 100644 (file)
@@ -25,9 +25,10 @@ __BEGIN_DECLS
 
 bool e4crypt_is_native();
 
-int e4crypt_policy_ensure(const char *directory,
-                          const char* policy, size_t policy_length,
-                          const char* contents_encryption_mode);
+int e4crypt_policy_ensure(const char *directory, const char *policy,
+                          size_t policy_length,
+                          const char *contents_encryption_mode,
+                          const char *filenames_encryption_mode);
 
 static const char* e4crypt_unencrypted_folder = "/unencrypted";
 static const char* e4crypt_key_ref = "/unencrypted/ref";
index 8cb9202..64b1fe3 100755 (executable)
@@ -21,6 +21,8 @@ E2FSDROID_OPTS=""
 if [ "$1" = "-s" ]; then
   MKE2FS_EXTENDED_OPTS+="android_sparse"
   shift
+else
+  E2FSDROID_OPTS+="-e"
 fi
 
 if [ $# -lt 5 ]; then
@@ -90,7 +92,7 @@ if [[ "$1" == "-i" ]]; then
 fi
 
 if [[ "$1" == "-e" ]]; then
-  if [[ MKE2FS_EXTENDED_OPTS ]]; then
+  if [[ $MKE2FS_EXTENDED_OPTS ]]; then
     MKE2FS_EXTENDED_OPTS+=","
   fi
   MKE2FS_EXTENDED_OPTS+="stripe_width=$(($2/BLOCKSIZE))"
@@ -98,7 +100,7 @@ if [[ "$1" == "-e" ]]; then
 fi
 
 if [[ "$1" == "-o" ]]; then
-  if [[ MKE2FS_EXTENDED_OPTS ]]; then
+  if [[ $MKE2FS_EXTENDED_OPTS ]]; then
     MKE2FS_EXTENDED_OPTS+=","
   fi
   # stride should be the max of 8kb and the logical block size
@@ -106,11 +108,13 @@ if [[ "$1" == "-o" ]]; then
   shift; shift
 fi
 
-if [[ MKE2FS_EXTENDED_OPTS ]]; then
+if [[ $MKE2FS_EXTENDED_OPTS ]]; then
   MKE2FS_OPTS+=" -E $MKE2FS_EXTENDED_OPTS"
 fi
 
-E2FSDROID_OPTS+=" -S $1"
+if [[ $1 ]]; then
+  E2FSDROID_OPTS+=" -S $1"
+fi
 
 case $EXT_VARIANT in
   ext4) ;;
@@ -122,6 +126,10 @@ if [ -z $MOUNT_POINT ]; then
   exit 2
 fi
 
+if [[ ${MOUNT_POINT:0:1} != "/" ]]; then
+  MOUNT_POINT="/"$MOUNT_POINT
+fi
+
 if [ -z $SIZE ]; then
   echo "Need size of filesystem"
   exit 2
@@ -130,6 +138,9 @@ fi
 # Round down the filesystem length to be a multiple of the block size
 SIZE=$((SIZE / BLOCKSIZE))
 
+# truncate output file since mke2fs will keep verity section in existing file
+cat /dev/null >$OUTPUT_FILE
+
 MAKE_EXT4FS_CMD="mke2fs $MKE2FS_OPTS -t $EXT_VARIANT -b $BLOCKSIZE $OUTPUT_FILE $SIZE"
 echo $MAKE_EXT4FS_CMD
 MKE2FS_CONFIG=./system/extras/ext4_utils/mke2fs.conf $MAKE_EXT4FS_CMD
@@ -137,7 +148,7 @@ if [ $? -ne 0 ]; then
   exit 4
 fi
 
-E2FSDROID_CMD="e2fsdroid $E2FSDROID_OPTS -f $SRC_DIR -a /$MOUNT_POINT $OUTPUT_FILE"
+E2FSDROID_CMD="e2fsdroid $E2FSDROID_OPTS -f $SRC_DIR -a $MOUNT_POINT $OUTPUT_FILE"
 echo $E2FSDROID_CMD
 $E2FSDROID_CMD
 if [ $? -ne 0 ]; then
diff --git a/ioshark/Android.mk b/ioshark/Android.mk
new file mode 100644 (file)
index 0000000..a647d75
--- /dev/null
@@ -0,0 +1,44 @@
+# 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_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := ioshark_bench.c ioshark_bench_subr.c ioshark_bench_mmap.c
+LOCAL_CFLAGS := -g -O2 -Wall  -Werror
+LOCAL_MODULE := ioshark_bench
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := compile_ioshark.c compile_ioshark_subr.c
+LOCAL_CFLAGS := -g -O2 -Wall -Werror -D_GNU_SOURCE
+LOCAL_MODULE := compile_ioshark
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := dump_ioshark_filenames.c
+LOCAL_CFLAGS := -g -O2 -Wall -Werror -D_GNU_SOURCE
+LOCAL_MODULE := dump_ioshark_filenames
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/ioshark/README b/ioshark/README
new file mode 100644 (file)
index 0000000..0c50fec
--- /dev/null
@@ -0,0 +1,30 @@
+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+ftrace compiler that takes straces and select ftraces 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.
+-s : One line summary.
+-q : Don't create the files in read-only partitions like /system and
+/vendor. Instead do reads on those files.
diff --git a/ioshark/collect-straces-ftraces.sh b/ioshark/collect-straces-ftraces.sh
new file mode 100644 (file)
index 0000000..6e83a24
--- /dev/null
@@ -0,0 +1,202 @@
+#!/bin/sh
+
+# This function just re-writes the timestamp of the strace entries to be
+# seconds.usecs since boot. To match the timestamping of ftrace (so we can
+# merge them later).
+process_strace()
+{
+    strace=$1
+    # parse in data/system/vendor and parse out /sys/devices/system/...
+    egrep '\/system\/|\/data\/|\/vendor\/' $strace | egrep -v '\/sys\/devices\/system\/' > bar
+    fgrep -v '= -1' bar > foo
+    mv foo bar
+    # begin_time is seconds since epoch
+    begin_time=`cat trace.begin`
+    # replace seconds since epoch with SECONDS SINCE BOOT in the
+    # strace files
+    awk -v begin="$begin_time" '{ printf "%f strace ", $1 - begin; $1=""; print $0}' bar > $2
+    rm bar
+}
+
+#
+# This function processes the ftrace file, removing the fields that we don't care
+# about, breaks up the ftrace file into one file per pid.
+# Input : One single fstrace file.
+# Output : Multiple fstrace.pid files.
+prep_fstrace()
+{
+    # Remove leading junk
+    fgrep android_fs_data $1 | sed 's/^.* \[.*\] //' | sed s/://g | sed s/,//g > foo
+    # Sanitize the filenames, removing spaces within the filename etc
+    sed 's/android_fs_dataread_start/read/' foo > bar1
+    mv bar1 bar
+    # First column is timestamp SECONDS SINCE BOOT
+    awk '{ print $2, "ftrace", $3, $5, $7, $9, $13 }' bar > foo
+    #awk '{ s ="" ; for (i=2; i <= NF ; i++) s = s $i " "; print s}' bar > foo
+    rm bar
+    # Get all the uniq pids
+    awk '{print $7}' foo | sort | uniq > pidlist
+    for i in `cat pidlist`
+    do
+       awk -v pid=$i '{ if (pid == $7) print $0}' foo > fstrace.$i
+    done
+    rm pidlist
+    rm foo
+}
+
+# Merge straces and ftraces.
+# The goal here is to catch mmap'ed IO (reads) that won't be in the
+# strace file. The algorithm is to look for mmaps in the strace file,
+# use the files tha are mmap'ed to search in the ftraces to pick up
+# tracepoints from there, and merge those with the straces.
+# The output of this function is a set of parsed_input_trace.<pid>
+# files, that can then be compiled into .wl files
+merge_compile()
+{
+    for stracefile in trace.*
+    do
+       if [ $stracefile == trace.begin ] || [ $stracefile == trace.tar ];
+       then
+           continue
+       fi
+       # Get the pid from the strace filename (pid is the extension)
+       pid=${stracefile##*.}
+       process_strace $stracefile foo.$pid
+       if ! [ -s foo.$pid ]; then
+           rm foo.$pid
+           continue
+       fi
+       #
+       # If we have matching strace and ftrace files, then look for mmaps in
+       # the strace pluck the corresponding entries for the mmap (mmaped IO)
+       # from the ftrace and merge them into the strace
+       #
+       if [ -f fstrace.$pid ]; then
+           fgrep mmap foo.$pid > bar
+           if [ -s bar ]; then
+               # Get all the unique mmap'ed filenames from the strace
+               awk '{ print $7 }' bar | sed 's/^[^<]*<//g' | sed 's/>,//g' > mapped_files
+               # Pluck all the lines from the ftrace corresponding to the mmaps
+               cat /dev/null > footemp
+               for j in `sort mapped_files | uniq`
+               do
+                   # Merge the readpage(s) traces from the ftrace into strace
+                   # for this mmaped file.
+                   grep -w $j fstrace.$pid > foobar
+                   if [ $? == 0 ]; then
+                       sort foo.$pid foobar >> footemp
+                   fi
+                   rm foobar
+               done
+               rm mapped_files
+               if [ -s footemp ]; then
+                   mv footemp parsed_input_trace.$pid
+               else
+                   mv foo.$pid parsed_input_trace.$pid
+               fi
+           else
+               mv foo.$pid parsed_input_trace.$pid
+           fi
+           rm bar
+       else
+           mv foo.$pid parsed_input_trace.$pid
+       fi
+       echo compiling parsed_input_trace.$pid
+       compile_ioshark parsed_input_trace.$pid $pid.wl
+       rm parsed_input_trace.$pid
+       rm -f foo.$pid
+    done
+}
+
+catch_sigint()
+{
+    echo "signal INT received, killing streaming trace capture"
+    ps_line=`ps -ef | grep trace_pipe | 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
+    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
+}
+
+enable_tracepoints()
+{
+    adb shell "echo 1 > /sys/kernel/debug/tracing/events/android_fs/android_fs_dataread_start/enable"
+    adb shell "echo 1 > /sys/kernel/debug/tracing/tracing_on"
+}
+
+disable_tracepoints()
+{
+    adb shell "echo 0 > /sys/kernel/debug/tracing/events/android_fs/android_fs_dataread_start/enable"
+    adb shell "echo 0 > /sys/kernel/debug/tracing/tracing_on"
+}
+
+kill_traces()
+{
+    ps_line=`ps -ef | grep trace_pipe | 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
+    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_traces
+}
+
+# main() starts here
+
+adb root && adb wait-for-device
+
+enable_tracepoints
+
+trap 'catch_sigint' INT
+
+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
+
+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" &
+adb shell "cat /sys/kernel/debug/tracing/trace_pipe" > fstrace &
+
+echo "^C this when done with the test"
+
+wait
+
+adb shell 'monkey -p com.android.alarmclock -p com.android.chrome -p com.android.calculator -p com.android.calendar -p com.google.android.calendar -p com.google.android.camera -p com.android.contacts -p com.google.android.gm -p com.android.im -p com.android.launcher -p com.google.android.apps.maps -p com.android.mms -p com.google.android.music -p com.android.phone -p com.google.android.youtube -p com.android.email -p com.google.android.voicesearch -c android.intent.category.LAUNCHER --throttle 200 --ignore-security-exceptions --ignore-crashes --ignore-timeouts -v -v -v 25000'
+
+kill_traces
+
+disable_tracepoints
+
+rm -f trace.*
+rm -f fstrace.*
+rm -f *.wl
+rm -f parsed*
+
+# Get the tracefiles from the device
+adb shell 'cd /data/local/tmp ; tar cvf trace.tar trace.*'
+adb pull /data/local/tmp/trace.tar
+tar xf trace.tar
+
+# Pre-process the ftrace file
+prep_fstrace fstrace
+# Merge the ftrace file(s) with the strace files
+merge_compile
+
+# tar up the .wl files just created
+tar cf wl.tar ioshark_filenames *.wl
diff --git a/ioshark/collect-straces.sh b/ioshark/collect-straces.sh
new file mode 100755 (executable)
index 0000000..21a3939
--- /dev/null
@@ -0,0 +1,97 @@
+# 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
+           rm bar
+           # begin_time is seconds since epoch
+           begin_time=`cat trace.begin`
+           # replace seconds since epoch with SECONDS SINCE BOOT in the
+           # strace files
+           awk -v begin="$begin_time" '{ printf "%f strace ", $1 - begin; $1=""; print $0}' foo > bar
+           if [ -s bar ]
+           then
+               echo parsing $i
+               pid=${i##*.}
+               compile_ioshark bar $pid.wl
+               rm -f bar
+           else
+               rm -f $i bar
+           fi
+       fi
+    done
+}
+
+# main() starts here
+
+adb root && adb wait-for-device
+
+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
+rm -f wl.tar
+tar cf wl.tar ioshark_filenames *.wl
diff --git a/ioshark/compile-only.sh b/ioshark/compile-only.sh
new file mode 100644 (file)
index 0000000..634b474
--- /dev/null
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+# This function just re-writes the timestamp of the strace entries to be
+# seconds.usecs since boot. To match the timestamping of ftrace (so we can
+# merge them later).
+process_strace()
+{
+    strace=$1
+    # parse in data/system/vendor and parse out /sys/devices/system/...
+    egrep '\/system\/|\/data\/|\/vendor\/' $strace | egrep -v '\/sys\/devices\/system\/' > bar
+    fgrep -v '= -1' bar > foo
+    mv foo bar
+    # begin_time is seconds since epoch
+    begin_time=`cat trace.begin`
+    # replace seconds since epoch with SECONDS SINCE BOOT in the
+    # strace files
+    awk -v begin="$begin_time" '{ printf "%f strace ", $1 - begin; $1=""; print $0}' bar > $2
+    rm bar
+}
+
+#
+# This function processes the ftrace file, removing the fields that we don't care
+# about, breaks up the ftrace file into one file per pid.
+# Input : One single fstrace file.
+# Output : Multiple fstrace.pid files.
+prep_fstrace()
+{
+    # Remove leading junk
+    fgrep android_fs_data $1 | sed 's/^.* \[.*\] //' | sed s/://g | sed s/,//g > foo
+    sed 's/android_fs_dataread_start/read/' foo > bar1
+    mv bar1 bar
+    # First column is timestamp SECONDS SINCE BOOT
+    awk '{ print $2, "ftrace", $3, $5, $7, $9, $13 }' bar > foo
+    rm bar
+    # Get all the uniq pids
+    awk '{print $7}' foo | sort | uniq > pidlist
+    for i in `cat pidlist`
+    do
+       awk -v pid=$i '{ if (pid == $7) print $0}' foo > fstrace.$i
+    done
+    rm pidlist
+    rm foo
+}
+
+# Merge straces and ftraces.
+# The goal here is to catch mmap'ed IO (reads) that won't be in the
+# strace file. The algorithm is to look for mmaps in the strace file,
+# use the files tha are mmap'ed to search in the ftraces to pick up
+# tracepoints from there, and merge those with the straces.
+# The output of this function is a set of parsed_input_trace.<pid>
+# files, that can then be compiled into .wl files
+merge_compile()
+{
+    for stracefile in trace.*
+    do
+       if [ $stracefile == trace.begin ] || [ $stracefile == trace.tar ];
+       then
+           continue
+       fi
+       # Get the pid from the strace filename (pid is the extension)
+       pid=${stracefile##*.}
+       process_strace $stracefile foo.$pid
+       if ! [ -s foo.$pid ]; then
+           rm foo.$pid
+           continue
+       fi
+       #
+       # If we have matching strace and ftrace files, then look for mmaps in
+       # the strace pluck the corresponding entries for the mmap (mmaped IO)
+       # from the ftrace and merge them into the strace
+       #
+       if [ -f fstrace.$pid ]; then
+           fgrep mmap foo.$pid > bar
+           if [ -s bar ]; then
+               # Get all the unique mmap'ed filenames from the strace
+               awk '{ print $7 }' bar | sed 's/^[^<]*<//g' | sed 's/>,//g' > mapped_files
+               # Pluck all the lines from the ftrace corresponding to the mmaps
+               cat /dev/null > footemp
+               for j in `sort mapped_files | uniq`
+               do
+                   # Merge the readpage(s) traces from the ftrace into strace
+                   # for this mmaped file.
+                   grep -w $j fstrace.$pid > foobar
+                   if [ $? == 0 ]; then
+                       sort foo.$pid foobar >> footemp
+                   fi
+                   rm foobar
+               done
+               rm mapped_files
+               if [ -s footemp ]; then
+                   mv footemp parsed_input_trace.$pid
+               else
+                   mv foo.$pid parsed_input_trace.$pid
+               fi
+           else
+               mv foo.$pid parsed_input_trace.$pid
+           fi
+           rm bar
+       else
+           mv foo.$pid parsed_input_trace.$pid
+       fi
+       echo compiling parsed_input_trace.$pid
+       compile_ioshark parsed_input_trace.$pid $pid.wl
+       rm parsed_input_trace.$pid
+       rm -f foo.$pid
+    done
+}
+
+# main() starts here
+
+rm -f *.wl
+rm -f parsed*
+rm ioshark_filenames
+
+# Pre-process the ftrace file
+prep_fstrace fstrace
+# Merge the ftrace file(s) with the strace files
+merge_compile
+
+# tar up the .wl files just created
+tar cf wl-test.tar ioshark_filenames *.wl
diff --git a/ioshark/compile_ioshark.c b/ioshark/compile_ioshark.c
new file mode 100644 (file)
index 0000000..f35d595
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+ * 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 },
+       { "ftrace", IOSHARK_MAPPED_PREAD }
+};
+
+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);
+}
+
+void
+init_prev_time(struct timeval *tv)
+{
+       tv->tv_sec = tv->tv_usec = 0;
+}
+
+/*
+ * delta ts is the time delta from the previous IO in this tracefile.
+ */
+static u_int64_t
+get_delta_ts(char *buf, struct timeval *prev)
+{
+       struct timeval op_tv, tv_res;
+
+       sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec);
+       /* First item */
+       if (prev->tv_sec == 0 && prev->tv_usec == 0)
+               tv_res.tv_sec = tv_res.tv_usec = 0;
+       else
+               timersub(&op_tv, prev, &tv_res);
+       *prev = op_tv;
+       return (tv_res.tv_usec + (tv_res.tv_sec * 1000000));
+}
+
+void
+get_tracetype(char *buf, char *trace_type)
+{
+       char *s, *s2;
+
+       *trace_type = '\0';
+       s = strchr(buf, ' ');
+       if (s == NULL) {
+               fprintf(stderr,
+                       "%s Malformed Trace Type ? %s\n",
+                       progname, __func__);
+               exit(EXIT_FAILURE);
+       }
+       while (*s == ' ')
+               s++;
+       if (sscanf(s, "%s", trace_type) != 1) {
+               fprintf(stderr,
+                       "%s Malformed Trace Type ? %s\n",
+                       progname, __func__);
+               exit(EXIT_FAILURE);
+       }
+       if (strcmp(trace_type, "strace") != 0 &&
+           strcmp(trace_type, "ftrace") != 0) {
+               fprintf(stderr,
+                       "%s Unknown/Missing Trace Type (has to be strace|ftrace) %s\n",
+                       progname, __func__);
+               exit(EXIT_FAILURE);
+       }
+       /*
+        * Remove the keyword "strace"/"ftrace" from the buffer
+        */
+       s2 = strchr(s, ' ');
+       if (s2 == NULL) {
+               fprintf(stderr,
+                       "%s Malformed Trace Type ? %s\n",
+                       progname, __func__);
+               exit(EXIT_FAILURE);
+       }
+       while (*s2 == ' ')
+               s2++;
+       if (*s2  == '\0') {
+               /*
+                * Premature end of input record
+                */
+               fprintf(stderr,
+                       "%s Mal-formed strace/ftrace record %s:%s\n",
+                       progname, __func__, buf);
+               exit(EXIT_FAILURE);
+       }
+       /* strcpy() expects non-overlapping buffers, but bcopy doesn't */
+       bcopy(s2, s, strlen(s2) + 1);
+}
+
+void
+get_pathname(char *buf, char *pathname, enum file_op file_op)
+{
+       char *s, *s2, save;
+
+       if (file_op == IOSHARK_MAPPED_PREAD) {
+               s = strchr(buf, '/');
+               if (s == NULL) {
+                       fprintf(stderr, "%s: Malformed line: %s\n",
+                               __func__, buf);
+                       exit(EXIT_FAILURE);
+               }
+               s2 = strchr(s, ' ');
+               if (s2 == NULL) {
+                       fprintf(stderr, "%s: Malformed line: %s\n",
+                               __func__, buf);
+                       exit(EXIT_FAILURE);
+               }
+       } else {
+               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_ftrace_offset_len(char *buf,
+                     off_t *offset,
+                     u_int64_t *len)
+{
+       char *s_offset;
+
+       s_offset = strchr(buf, '/');
+       if (s_offset == NULL) {
+               fprintf(stderr, "%s: Malformed line 1: %s\n",
+                       __func__, buf);
+               exit(EXIT_FAILURE);
+       }
+       s_offset = strchr(s_offset, ' ');
+       if (s_offset == NULL) {
+               fprintf(stderr, "%s: Malformed line 2: %s\n",
+                       __func__, buf);
+               exit(EXIT_FAILURE);
+       }
+       while (*s_offset == ' ')
+               s_offset++;
+       if (sscanf(s_offset, "%ju %ju", offset, len) != 2) {
+               fprintf(stderr, "%s: Malformed line 3: %s\n",
+                       __func__, buf);
+               exit(EXIT_FAILURE);
+       }
+}
+
+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));
+}
+
+/*
+ * 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 prev_time;
+       char trace_type[64];
+
+       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);
+       }
+       init_prev_time(&prev_time);
+       init_filename_cache();
+       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, &prev_time);
+               get_tracetype(s, trace_type);
+               if (strcmp(trace_type, "strace") == 0) {
+                       get_syscall(s, syscall);
+                       disk_file_op->file_op = map_syscall(syscall);
+               } else
+                       disk_file_op->file_op = map_syscall("ftrace");
+               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;
+               case IOSHARK_MAPPED_PREAD:
+                       /* Convert a mmap'ed read into a PREAD64 */
+                       disk_file_op->file_op = IOSHARK_PREAD64;
+                       get_ftrace_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;
+               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);
+       }
+       store_filename_cache();
+}
diff --git a/ioshark/compile_ioshark.h b/ioshark/compile_ioshark.h
new file mode 100644 (file)
index 0000000..9e11bd2
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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;
+       int     global_filename_ix;
+};
+
+/* 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);
+void init_filename_cache(void);
+void store_filename_cache(void);
+
+
+
diff --git a/ioshark/compile_ioshark_subr.c b/ioshark/compile_ioshark_subr.c
new file mode 100644 (file)
index 0000000..5cc07c6
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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;
+
+static int filename_cache_lookup(char *filename);;
+
+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;
+                       st.global_filename_ix =
+                               db_node->global_filename_ix;
+                       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->global_filename_ix =
+               filename_cache_lookup(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;
+}
+
+static struct ioshark_filename_struct *filename_cache;
+static int filename_cache_num_entries;
+static int filename_cache_size;
+
+void
+init_filename_cache(void)
+{
+       static FILE *filename_cache_fp;
+       struct stat st;
+       int file_exists = 1;
+
+       if (stat("ioshark_filenames", &st) < 0) {
+               if (errno != ENOENT) {
+                       fprintf(stderr, "%s Can't stat ioshark_filenames file\n",
+                               progname);
+                       exit(EXIT_FAILURE);
+               } else {
+                       file_exists = 0;
+                       filename_cache_num_entries = 0;
+               }
+       } else {
+               filename_cache_num_entries = st.st_size /
+                       sizeof(struct ioshark_filename_struct);
+       }
+       if (file_exists) {
+               filename_cache_fp = fopen("ioshark_filenames", "r");
+               if (filename_cache_fp == NULL) {
+                       fprintf(stderr, "%s Cannot open ioshark_filenames file\n",
+                               progname);
+                       exit(EXIT_FAILURE);
+               }
+       }
+       /* Preallocate a fixed size of entries */
+       filename_cache_size = filename_cache_num_entries + 1024;
+       filename_cache = calloc(filename_cache_size,
+                               sizeof(struct ioshark_filename_struct));
+       if (filename_cache == NULL) {
+               fprintf(stderr, "%s Can't allocate memory - this is fatal\n",
+                       __func__);
+               exit(EXIT_FAILURE);
+       }
+       if (fread(filename_cache,
+                 sizeof(struct ioshark_filename_struct),
+                 filename_cache_num_entries,
+                 filename_cache_fp) != (size_t)filename_cache_num_entries) {
+               fprintf(stderr, "%s Can't read ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       if (file_exists)
+               fclose(filename_cache_fp);
+}
+
+static int
+filename_cache_lookup(char *filename)
+{
+       int ret;
+       int i;
+
+       for (i = 0 ; i < filename_cache_num_entries ; i++) {
+               if (strcmp(filename_cache[i].path, filename) == 0)
+                       return i;
+       }
+       if (filename_cache_num_entries >= filename_cache_size) {
+               int newsize;
+
+               /* reallocate the filename cache up first */
+               filename_cache_size += 1024;
+               newsize = filename_cache_size *
+                       sizeof(struct ioshark_filename_struct);
+               filename_cache = realloc(filename_cache, newsize);
+               if (filename_cache == NULL) {
+                       fprintf(stderr,
+                               "%s Can't allocate memory - this is fatal\n",
+                               __func__);
+                       exit(EXIT_FAILURE);
+               }
+       }
+       strcpy(filename_cache[filename_cache_num_entries].path,
+              filename);
+       ret = filename_cache_num_entries;
+       filename_cache_num_entries++;
+       return ret;
+}
+
+void
+store_filename_cache(void)
+{
+       static FILE *filename_cache_fp;
+
+       filename_cache_fp = fopen("ioshark_filenames", "w+");
+       if (filename_cache_fp == NULL) {
+               fprintf(stderr, "%s Cannot open ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       if (fwrite(filename_cache,
+                  sizeof(struct ioshark_filename_struct),
+                  filename_cache_num_entries,
+                  filename_cache_fp) != (size_t)filename_cache_num_entries) {
+               fprintf(stderr, "%s Can't read ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       fclose(filename_cache_fp);
+       free(filename_cache);
+}
+
+
+
diff --git a/ioshark/dump_ioshark_filenames.c b/ioshark/dump_ioshark_filenames.c
new file mode 100644 (file)
index 0000000..c082c27
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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"
+
+/*
+ * Real simple utility that just extracts and dumps the IOshark filenames
+ * one per line from the ioshark_filenames file. Useful in debugging.
+ */
+int
+main(int argc __attribute__((unused)), char **argv)
+{
+       char *progname;
+       static FILE *filename_cache_fp;
+       struct stat st;
+       struct ioshark_filename_struct *filename_cache;
+       int filename_cache_num_entries;
+       size_t filename_cache_size;
+       int i;
+
+       progname = argv[0];
+       if (stat("ioshark_filenames", &st) < 0) {
+               fprintf(stderr, "%s Can't stat ioshark_filenames file\n",
+                       progname);
+                       exit(EXIT_FAILURE);
+       }
+       filename_cache_num_entries = st.st_size /
+               sizeof(struct ioshark_filename_struct);
+       filename_cache_fp = fopen("ioshark_filenames", "r");
+       if (filename_cache_fp == NULL) {
+               fprintf(stderr, "%s Cannot open ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       /* Preallocate a fixed size of entries */
+       filename_cache_size = filename_cache_num_entries + 1024;
+       filename_cache = calloc(filename_cache_size,
+                               sizeof(struct ioshark_filename_struct));
+       if (filename_cache == NULL) {
+               fprintf(stderr, "%s Can't allocate memory - this is fatal\n",
+                       __func__);
+               exit(EXIT_FAILURE);
+       }
+       if (fread(filename_cache,
+                 sizeof(struct ioshark_filename_struct),
+                 filename_cache_num_entries,
+                 filename_cache_fp) != (size_t)filename_cache_num_entries) {
+               fprintf(stderr, "%s Can't read ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       for (i = 0 ; i < filename_cache_num_entries ; i++) {
+              printf("%s\n", filename_cache[i].path);
+       }
+       free(filename_cache);
+       fclose(filename_cache_fp);
+}
diff --git a/ioshark/ioshark.h b/ioshark/ioshark.h
new file mode 100644 (file)
index 0000000..fd9caca
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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;
+       int     global_filename_ix;
+};
+
+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;
+};
+
+#define MAX_IOSHARK_PATHLEN    512
+
+/*
+ * Global table of all fileames
+ */
+struct ioshark_filename_struct
+{
+       char path[MAX_IOSHARK_PATHLEN];
+};
diff --git a/ioshark/ioshark_bench.c b/ioshark/ioshark_bench.c
new file mode 100644 (file)
index 0000000..f44e73e
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/statfs.h>
+#include <sys/resource.h>
+#include "ioshark.h"
+#define IOSHARK_MAIN
+#include "ioshark_bench.h"
+
+/*
+ * Note on "quick" mode where we do reads on existing /system,
+ * /vendor and other files in ro partitions, instead of creating
+ * them. The ioshark compiler builds up a table of all the files
+ * in /system, /vendor and other ro partitions. For files in this
+ * list, the benchmark skips the pre-creation of these files and
+ * reads them directly.
+ * The code relevant to this is in *filename_cache*.
+ */
+
+char *progname;
+
+#define MAX_INPUT_FILES                8192
+#define MAX_THREADS            8192
+
+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;
+int summary_mode = 0;
+int quick_mode = 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] -q -v | -s <list of parsed input files>\n",
+               progname);
+       fprintf(stderr, "%s -s, -v are mutually exclusive\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[MAX_IOSHARK_PATHLEN];
+       void *db_node;
+       struct rw_bytes_s rw_bytes;
+       char *filename;
+       int readonly;
+
+       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);
+               }
+               /*
+                * Check to see if the file is in a readonly partition,
+                * in which case, we don't have to pre-create the file
+                * we can just read the existing file.
+                */
+               filename =
+                       get_ro_filename(file_state.global_filename_ix);
+               if (quick_mode)
+                       assert(filename != NULL);
+               if (quick_mode == 0 ||
+                   is_readonly_mount(filename, file_state.size) == 0) {
+                       sprintf(path, "file.%d.%d",
+                               (int)(state - thread_state),
+                               file_state.fileno);
+                       create_file(path, file_state.size,
+                                   &rw_bytes);
+                       filename = path;
+                       readonly = 0;
+               } else {
+                       readonly = 1;
+               }
+               db_node = files_db_add_byfileno(state->db_handle,
+                                               file_state.fileno,
+                                               readonly);
+               files_db_update_size(db_node, file_state.size);
+               files_db_update_filename(db_node, filename);
+       }
+       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;
+       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);
+                       update_delta_time(&start, &total_delay_time);
+               }
+               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) {
+                       int openflags;
+
+                       /*
+                        * 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.
+                        */
+                       if (files_db_readonly(db_node))
+                               openflags = O_RDONLY;
+                       else
+                               openflags = O_RDWR;
+                       fd = open(files_db_get_filename(db_node),
+                                 openflags);
+                       if (fd < 0) {
+                               fprintf(stderr, "%s: open(%s %x) error %d\n",
+                                       progname,
+                                       files_db_get_filename(db_node),
+                                       openflags,
+                                       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 (quick_mode == 0 ||
+                           !is_readonly_mount(
+                                   get_ro_filename(file_state.global_filename_ix),
+                                   file_state.size)) {
+                               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;
+       }
+}
+
+#define IOSHARK_FD_LIM         8192
+
+static void
+sizeup_fd_limits(void)
+{
+       struct rlimit r;
+
+       getrlimit(RLIMIT_NOFILE, &r);
+       if (r.rlim_cur >= IOSHARK_FD_LIM)
+               /* cur limit already at what we want */
+               return;
+       /*
+        * Size up both the Max and Cur to IOSHARK_FD_LIM.
+        * If we are not running as root, this will fail,
+        * catch that below and exit.
+        */
+       if (r.rlim_max < IOSHARK_FD_LIM)
+               r.rlim_max = IOSHARK_FD_LIM;
+       r.rlim_cur = IOSHARK_FD_LIM;
+       if (setrlimit(RLIMIT_NOFILE, &r) < 0) {
+               fprintf(stderr, "%s: Can't setrlimit (RLIMIT_NOFILE, 8192)\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       getrlimit(RLIMIT_NOFILE, &r);
+       if (r.rlim_cur < IOSHARK_FD_LIM) {
+               fprintf(stderr, "%s: Can't setrlimit up to 8192\n",
+                       progname);
+               fprintf(stderr, "%s: Running as root ?\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+}
+
+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:st:qv")) != EOF) {
+                switch (c) {
+                case 'd':
+                       do_delay = 1;
+                       break;
+                case 'n':
+                       num_iterations = atoi(optarg);
+                       break;
+                case 's':
+                       /* Non-verbose summary mode for nightly runs */
+                       summary_mode = 1;
+                       break;
+                case 't':
+                       num_threads = atoi(optarg);
+                       break;
+                case 'q':
+                       /*
+                        * If quick mode is enabled, then we won't
+                        * pre-create files that we are doing IO on that
+                        * live in readonly partitions (/system, /vendor etc)
+                        */
+                       quick_mode = 1;
+                       break;
+                case 'v':
+                       verbose = 1;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       if ((verbose + summary_mode) == 2)
+               usage();
+
+       if (num_threads > MAX_THREADS)
+               usage();
+
+       if (optind == argc)
+                usage();
+
+       sizeup_fd_limits();
+
+       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);
+               }
+               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);
+
+       if (quick_mode)
+               init_filename_cache();
+
+       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 */
+               if (!summary_mode)
+                       printf("Doing Pre-creation of Files\n");
+               if (quick_mode && !summary_mode)
+                       printf("Skipping Pre-creation of read-only 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 (!summary_mode) {
+                               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);
+               }
+       }
+       if (!summary_mode) {
+               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();
+       } else {
+               printf("%ju.%ju ",
+                      get_msecs(&aggregate_file_create_time),
+                      get_usecs(&aggregate_file_create_time));
+               printf("%ju.%ju ",
+                      get_msecs(&aggregate_file_remove_time),
+                      get_usecs(&aggregate_file_remove_time));
+               if (do_delay)
+                       printf("%ju.%ju ",
+                              get_msecs(&aggregate_delay_time),
+                              get_usecs(&aggregate_delay_time));
+               printf("%ju.%ju ",
+                      get_msecs(&aggregate_IO_time),
+                      get_usecs(&aggregate_IO_time));
+               print_bytes(NULL, &aggr_io_rw_bytes);
+               report_cpu_disk_util();
+               printf("\n");
+       }
+       if (quick_mode)
+               free_filename_cache();
+}
diff --git a/ioshark/ioshark_bench.h b/ioshark/ioshark_bench.h
new file mode 100644 (file)
index 0000000..d0d0962
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 readonly;
+       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 int
+files_db_readonly(void *node)
+{
+       return (((struct files_db_s *)node)->readonly);
+}
+
+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, int readonly);
+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);
+
+char *get_ro_filename(int ix);
+void init_filename_cache(void);
+void free_filename_cache(void);
+int is_readonly_mount(char *filename, size_t size);
diff --git a/ioshark/ioshark_bench_mmap.c b/ioshark/ioshark_bench_mmap.c
new file mode 100644 (file)
index 0000000..55d7060
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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 "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 (file)
index 0000000..4280a5a
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * 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 <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/mman.h>
+#include "ioshark.h"
+#include "ioshark_bench.h"
+
+extern char *progname;
+extern int verbose, summary_mode;
+
+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, int readonly)
+{
+       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->readonly = readonly;
+               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;
+                               int openflags;
+
+                               /*n
+                                * File was closed, let's open it so we can
+                                * fsync and fadvise(DONTNEED) it.
+                                */
+                               do_close = 1;
+                               if (files_db_readonly(db_node))
+                                       openflags = O_RDONLY;
+                               else
+                                       openflags = O_RDWR;
+                               fd = open(files_db_get_filename(db_node),
+                                         openflags);
+                               if (fd < 0) {
+                                       fprintf(stderr,
+                                               "%s: open(%s %x) error %d\n",
+                                               progname, db_node->filename,
+                                               openflags,
+                                               errno);
+                                       exit(EXIT_FAILURE);
+                               }
+                               db_node->fd = fd;
+                       }
+                       if (!db_node->readonly && 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 (is_readonly_mount(db_node->filename, db_node->size) == 0) {
+                               if (unlink(db_node->filename) < 0) {
+                                       fprintf(stderr, "%s: Cannot unlink %s:%s\n",
+                                               __func__, db_node->filename,
+                                               strerror(errno));
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+                       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)
+{
+       if (!summary_mode)
+               printf("%s: Reads = %dMB, Writes = %dMB\n",
+                      desc,
+                      (int)(rw_bytes->bytes_read / (1024 * 1024)),
+                      (int)(rw_bytes->bytes_written / (1024 * 1024)));
+       else
+               printf("%d %d ",
+                      (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
+get_blockdev_name(char *bdev)
+{
+       char dev_name[BUFSIZE];
+       FILE *cmd;
+
+       cmd = popen("getprop ro.product.name", "r");
+       if (cmd == NULL) {
+               fprintf(stderr, "%s: Cannot popen getprop\n",
+                       progname);
+               exit(1);
+       }
+       if (fgets(dev_name, BUFSIZE, cmd) == NULL) {
+               fprintf(stderr,
+                       "%s: Bad output from getprop ro.product.name\n",
+                       progname);
+               exit(1);
+       }
+       pclose(cmd);
+       /* strncmp needed because of the trailing '\n' */
+       if (strncmp(dev_name, "bullhead", strlen("bullhead")) == 0 ||
+           strncmp(dev_name, "angler", strlen("angler")) == 0 ||
+           strncmp(dev_name, "shamu", strlen("shamu")) == 0) {
+               strcpy(bdev, "mmcblk0");
+       } else if (strncmp(dev_name, "marlin", strlen("marlin")) == 0 ||
+                  strncmp(dev_name, "sailfish", strlen("sailfish")) == 0) {
+               strcpy(bdev, "sda");
+       } else {
+               fprintf(stderr,
+                       "%s: Unknown device %s\n",
+                       progname, dev_name);
+               exit(1);
+       }
+}
+
+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;
+       char blockdev_name[BUFSIZE];
+
+       /* 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 */
+       get_blockdev_name(blockdev_name);
+       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, blockdev_name) == 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);
+}
+
+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;
+       if (!summary_mode)
+               printf("CPU util = %.2f%%\n", cpu_util);
+       else
+               printf("%.2f ", 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;
+       if (!summary_mode)
+               printf("User CPU util = %.2f%%\n", cpu_util);
+       else
+               printf("%.2f ", 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;
+       if (!summary_mode)
+               printf("System CPU util = %.2f%%\n", cpu_util);
+       else
+               printf("%.2f ", 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);
+       }
+       if (!summary_mode)
+               printf("Disk util = %.2f%%\n", disk_util);
+       else
+               printf("%.2f", disk_util);
+}
+
+
+static struct ioshark_filename_struct *filename_cache;
+static int filename_cache_num_entries;
+
+char *
+get_ro_filename(int ix)
+{
+       if (ix >= filename_cache_num_entries)
+               return NULL;
+       return filename_cache[ix].path;
+}
+
+void
+init_filename_cache(void)
+{
+       int fd;
+       struct stat st;
+
+       fd = open("ioshark_filenames", O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "%s Can't open ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       if (fstat(fd, &st) < 0) {
+               fprintf(stderr, "%s Can't fstat ioshark_filenames file\n",
+                       progname);
+               exit(EXIT_FAILURE);
+       }
+       filename_cache_num_entries = st.st_size /
+               sizeof(struct ioshark_filename_struct);
+       filename_cache = mmap(NULL, st.st_size, PROT_READ,
+                             MAP_SHARED | MAP_LOCKED | MAP_POPULATE,
+                             fd, 0);
+       if (filename_cache == MAP_FAILED) {
+               fprintf(stderr, "%s Can't fstat ioshark_filenames file: %s\n",
+                       progname, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       close(fd);
+}
+
+void
+free_filename_cache(void)
+{
+       size_t mmap_size;
+
+       mmap_size = filename_cache_num_entries *
+               sizeof(struct ioshark_filename_struct);
+       munmap(filename_cache, mmap_size);
+}
+
+/*
+ * Is the passed in filename a regular file ? (eg. not a directory).
+ * Second, is it in a read-only partition ?
+ */
+int
+is_readonly_mount(char *filename, size_t size)
+{
+       struct statfs statfsbuf;
+       struct stat statbuf;
+
+       if (stat(filename, &statbuf) < 0) {
+               /* File possibly deleted */
+               return 0;
+       }
+       if (!S_ISREG(statbuf.st_mode)) {
+               /* Is it a regular file ? */
+               return 0;
+       }
+       if ((size_t)statbuf.st_size < size) {
+               /* Size of existing file is smaller than we expect */
+               return 0;
+       }
+       if (statfs(filename, &statfsbuf) < 0) {
+               /* This shouldn't happen */
+               return 0;
+       }
+       if ((statfsbuf.f_flags & ST_RDONLY) == 0)
+               return 0;
+       else
+               return 1;
+}
diff --git a/ioshark/monkey-strace+fstrace.tgz b/ioshark/monkey-strace+fstrace.tgz
new file mode 100644 (file)
index 0000000..6f02301
Binary files /dev/null and b/ioshark/monkey-strace+fstrace.tgz differ
diff --git a/ioshark/monkeystracebig.tgz b/ioshark/monkeystracebig.tgz
new file mode 100644 (file)
index 0000000..e2ab7bc
Binary files /dev/null and b/ioshark/monkeystracebig.tgz differ
diff --git a/ioshark/monkeytracebig.tar.gz b/ioshark/monkeytracebig.tar.gz
new file mode 100644 (file)
index 0000000..2ff7047
Binary files /dev/null and b/ioshark/monkeytracebig.tar.gz differ
diff --git a/ioshark/wl.tar b/ioshark/wl.tar
new file mode 100644 (file)
index 0000000..a004ad1
Binary files /dev/null and b/ioshark/wl.tar differ
index 20d060b..2dfd900 100644 (file)
@@ -7,14 +7,20 @@
 #include <memory>
 #include <cmath>
 #include <string>
+#include <thread>
+
+#define CACHE_HIT_SIZE 1 << 17
 
 using namespace std;
 
-const size_t size_start = 64;
-const size_t size_end = 16 * (1ull << 20);
-const size_t samples = 2048;
+size_t size_start = 64;
+size_t size_end = 16 * (1ull << 20);
+size_t samples = 2048;
 size_t size_per_test = 64 * (1ull << 20);
 size_t tot_sum = 0;
+size_t delay = 0;
+float speed = 0;
+bool dummy = false;
 
 void __attribute__((noinline)) memcpy_noinline(void *dst, void *src, size_t size);
 void __attribute__((noinline)) memset_noinline(void *dst, int value, size_t size);
@@ -26,21 +32,64 @@ enum BenchType {
     SumBench,
 };
 
+static void usage(char* p) {
+    printf("Usage: %s <test> <options>\n"
+           "<test> is one of the following:\n"
+           "  --memcpy\n"
+           "  --memset\n"
+           "  --sum\n"
+           "<options> are optional and apply to all tests:\n"
+           "  --dummy\n"
+           "    Simulates cpu-only load of a test. Guaranteed to use L2\n"
+           "    instead.  Not supported on --sum test.\n"
+           "  --delay DELAY_DIVISOR\n"
+           "  --start START_SIZE_MB\n"
+           "    --end END_SIZE_MB (requires start, optional)\n"
+           "  --samples NUM_SAMPLES\n"
+           , p);
+}
+
 int main(int argc, char *argv[])
 {
-    BenchType type;
+    BenchType type = MemcpyBench;
     if (argc <= 1) {
-        cerr << "memcpy_perf [--memcpy|--memset|--sum]" << endl;
+        usage(argv[0]);
         return 0;
     }
-    if (string(argv[1]) == string("--memcpy")) {
-        type = MemcpyBench;
-    } else if (string(argv[1]) == string("--memset")) {
-        type = MemsetBench;
-    } else if (string(argv[1]) == string("--sum")) {
-        type = SumBench;
-    } else {
-        type = MemcpyBench;
+    for (int i = 1; i < argc; i++) {
+      if (string(argv[i]) == string("--memcpy")) {
+         type = MemcpyBench;
+      } else if (string(argv[i]) == string("--memset")) {
+         type = MemsetBench;
+      } else if (string(argv[i]) == string("--sum")) {
+         type = SumBench;
+      } else if (string(argv[i]) == string("--dummy")) {
+         dummy = true;
+      } else if (i + 1 < argc) {
+          if (string(argv[i]) == string("--delay")) {
+             delay = atoi(argv[++i]);
+          } else if (string(argv[i]) == string("--start")) {
+             size_start = atoi(argv[++i]) * (1ull << 20);
+             size_end = size_start;
+          } else if (string(argv[i]) == string("--end")) {
+             size_t end = atoi(argv[++i]) * (1ull << 20);
+             if (end > size_start && i > 3
+                 && string(argv[i-3]) == string("--start")) {
+                 size_end = end;
+             } else {
+                 printf("Cannot specify --end without --start.\n");
+                 return 0;
+             }
+          } else if (string(argv[i]) == string("--samples")) {
+             samples = atoi(argv[++i]);
+          } else {
+             printf("Unknown argument %s\n", argv[i]);
+             return 0;
+          }
+       } else {
+          printf("The %s option requires a single argument.\n", argv[i]);
+          return 0;
+       }
     }
 
     unique_ptr<uint8_t[]> src(new uint8_t[size_end]);
@@ -54,8 +103,10 @@ int main(int argc, char *argv[])
     //cout << "src: " << (uintptr_t)src.get() << endl;
     //cout << "dst: " <<  (uintptr_t)dst.get() << endl;
 
-    for (double cur_pow = start_pow; cur_pow <= end_pow; cur_pow += pow_inc) {
-        chrono::time_point<chrono::high_resolution_clock> copy_start, copy_end;
+    for (double cur_pow = start_pow; cur_pow <= end_pow && samples > 0;
+            cur_pow += pow_inc) {
+        chrono::time_point<chrono::high_resolution_clock>
+            copy_start, copy_end, pre_wait;
 
         size_t cur_size = (size_t)pow(10.0, cur_pow);
         size_t iter_per_size = size_per_test / cur_size;
@@ -65,9 +116,21 @@ int main(int argc, char *argv[])
             case MemsetBench: {
                 memcpy_noinline(src.get(), dst.get(), cur_size);
                 memset_noinline(dst.get(), 0xdeadbeef, cur_size);
+                size_t hit_size = CACHE_HIT_SIZE;
                 copy_start = chrono::high_resolution_clock::now();
                 for (int i = 0; i < iter_per_size; i++) {
-                    memset_noinline(dst.get(), 0xdeadbeef, cur_size);
+                    if (!dummy) {
+                        memset_noinline(dst.get(), 0xdeadbeef, cur_size);
+                    } else {
+                        while (hit_size < cur_size) {
+                            memset_noinline
+                                (dst.get(), 0xdeadbeef, CACHE_HIT_SIZE);
+                            hit_size += 1 << 17;
+                        }
+                    }
+                    if (delay != 0)
+                        this_thread::sleep_for(chrono
+                            ::nanoseconds(size_per_test / delay));
                 }
                 copy_end = chrono::high_resolution_clock::now();
                 break;
@@ -75,9 +138,21 @@ int main(int argc, char *argv[])
             case MemcpyBench: {
                 memcpy_noinline(dst.get(), src.get(), cur_size);
                 memcpy_noinline(src.get(), dst.get(), cur_size);
+                size_t hit_size = CACHE_HIT_SIZE;
                 copy_start = chrono::high_resolution_clock::now();
                 for (int i = 0; i < iter_per_size; i++) {
-                    memcpy_noinline(dst.get(), src.get(), cur_size);
+                    if (!dummy) {
+                        memcpy_noinline(dst.get(), src.get(), cur_size);
+                    } else {
+                        while (hit_size < cur_size) {
+                            memcpy_noinline
+                                (dst.get(), src.get(), CACHE_HIT_SIZE);
+                            hit_size += CACHE_HIT_SIZE;
+                        }
+                    }
+                    if (delay != 0)
+                        this_thread::sleep_for(chrono
+                            ::nanoseconds(size_per_test / delay));
                 }
                 copy_end = chrono::high_resolution_clock::now();
                 break;
@@ -88,6 +163,9 @@ int main(int argc, char *argv[])
                 copy_start = chrono::high_resolution_clock::now();
                 for (int i = 0; i < iter_per_size; i++) {
                     s += sum(src.get(), cur_size);
+                    if (delay != 0)
+                        this_thread::sleep_for(chrono
+                            ::nanoseconds(size_per_test / delay));
                 }
                 copy_end = chrono::high_resolution_clock::now();
                 tot_sum += s;
@@ -95,11 +173,18 @@ int main(int argc, char *argv[])
             }
         }
 
+        samples--;
         double ns_per_copy = chrono::duration_cast<chrono::nanoseconds>(copy_end - copy_start).count() / double(iter_per_size);
         double gb_per_sec = ((double)cur_size / (1ull<<30)) / (ns_per_copy / 1.0E9);
         if (type == MemcpyBench)
             gb_per_sec *= 2.0;
-        cout << "size: " << cur_size << ", perf: " << gb_per_sec << "GB/s, iter: " << iter_per_size << endl;
+        double percent_waiting = 0;
+        if (delay != 0) {
+            percent_waiting = (size_per_test / delay) / ns_per_copy * 100;
+        }
+        cout << "size: " << cur_size << ", perf: " << gb_per_sec
+             << "GB/s, iter: " << iter_per_size << ", \% time spent waiting: "
+             << percent_waiting << endl;
     }
     return 0;
 }
index dfdc63f..f9a12ff 100644 (file)
 #endif
 
 static const char* kDalvikCacheDir = "/data/dalvik-cache/";
-static const char* kCacheSuffix = "@classes.dex";
+static const char* kOdexCacheSuffix = "@classes.dex";
+static const char* kVdexCacheSuffix = "@classes.vdex";
 
-// Returns the ISA extracted from the odex_file_location.
-// odex_file_location is formatted like /system/app/<app_name>/oat/<isa>/<app_name>.odex for all
-// functions. We return an empty string "" in error cases.
-static std::string ExtractISA(const std::string& odex_file_location) {
-  std::vector<std::string> split_file_location = android::base::Split(odex_file_location, "/");
+// Returns the ISA extracted from the file_location.
+// file_location is formatted like /system/app/<app_name>/oat/<isa>/<app_name>.{odex,vdex}
+// for all functions. We return an empty string "" in error cases.
+static std::string ExtractISA(const std::string& file_location) {
+  std::vector<std::string> split_file_location = android::base::Split(file_location, "/");
   if (split_file_location.size() <= 1) {
     return "";
   } else if (split_file_location.size() != 7) {
-    LOG(WARNING) << "Unexpected length for odex-file-location. We expected 7 segments but found "
+    LOG(WARNING) << "Unexpected length for file-location. We expected 7 segments but found "
                  << split_file_location.size();
   }
   return split_file_location[split_file_location.size() - 2];
 }
 
-// Returns the apk name extracted from the odex_file_location.
-// odex_file_location is formatted like /system/app/<app_name>/oat/<isa>/<app_name>.odex. We return
-// the final <app_name> with the .odex replaced with .apk.
-static std::string ExtractAPKName(const std::string& odex_file_location) {
+// Returns the apk name extracted from the file_location.
+// file_location is formatted like /system/app/<app_name>/oat/<isa>/<app_name>.{odex,vdex}.
+// We return the final <app_name> with the .{odex,vdex} replaced with .apk.
+static std::string ExtractAPKName(const std::string& file_location) {
   // Find and copy filename.
-  size_t file_location_start = odex_file_location.rfind('/');
+  size_t file_location_start = file_location.rfind('/');
   if (file_location_start == std::string::npos) {
     return "";
   }
-  size_t ext_start = odex_file_location.rfind('.');
+  size_t ext_start = file_location.rfind('.');
   if (ext_start == std::string::npos || ext_start < file_location_start) {
     return "";
   }
-  std::string apk_name = odex_file_location.substr(file_location_start + 1,
-                                                   ext_start - file_location_start);
+  std::string apk_name = file_location.substr(file_location_start + 1,
+                                              ext_start - file_location_start);
 
   // Replace extension with .apk.
   apk_name += "apk";
@@ -62,18 +63,18 @@ static std::string ExtractAPKName(const std::string& odex_file_location) {
 }
 
 // The cache file name is /data/dalvik-cache/<isa>/ prior to this function
-static bool OdexFilenameToCacheFile(const std::string& odex_file_location,
-                                    /*in-out*/std::string& cache_file) {
-  // Skip the first '/' in odex_file_location.
-  size_t initial_position = odex_file_location[0] == '/' ? 1 : 0;
-  size_t apk_position = odex_file_location.find("/oat", initial_position);
+static bool SystemBFilenameToCacheFile(const std::string& file_location,
+                                       /*in-out*/std::string& cache_file) {
+  // Skip the first '/' in file_location.
+  size_t initial_position = file_location[0] == '/' ? 1 : 0;
+  size_t apk_position = file_location.find("/oat", initial_position);
   if (apk_position == std::string::npos) {
     LOG(ERROR) << "Unable to find oat directory!";
     return false;
   }
 
   size_t cache_file_position = cache_file.size();
-  cache_file += odex_file_location.substr(initial_position, apk_position);
+  cache_file += file_location.substr(initial_position, apk_position);
   // '/' -> '@' up to where the apk would be.
   cache_file_position = cache_file.find('/', cache_file_position);
   while (cache_file_position != std::string::npos) {
@@ -82,28 +83,33 @@ static bool OdexFilenameToCacheFile(const std::string& odex_file_location,
   }
 
   // Add <apk_name>.
-  std::string apk_name = ExtractAPKName(odex_file_location);
+  std::string apk_name = ExtractAPKName(file_location);
   if (apk_name.empty()) {
-    LOG(ERROR) << "Unable to determine apk name from odex file name '" << odex_file_location << "'";
+    LOG(ERROR) << "Unable to determine apk name from file name '" << file_location << "'";
     return false;
   }
   cache_file += apk_name;
-  cache_file += kCacheSuffix;
+  if (file_location.size() >= 5 &&
+      file_location.substr(file_location.size() - 5) == std::string(".vdex")) {
+    cache_file += kVdexCacheSuffix;
+  } else {
+    cache_file += kOdexCacheSuffix;
+  }
   return true;
 }
 
-// Do the overall transformation from odex_file_location to output_file_location. Prior to this the
+// Do the overall transformation from file_location to output_file_location. Prior to this the
 // output_file_location is empty.
-static bool OdexToCacheFile(std::string& odex_file_location,
-                            /*out*/std::string& output_file_location) {
-  std::string isa = ExtractISA(odex_file_location);
+static bool SystemBFileToCacheFile(const std::string& file_location,
+                                   /*out*/std::string& output_file_location) {
+  std::string isa = ExtractISA(file_location);
   if (isa.empty()) {
-    LOG(ERROR) << "Unable to determine isa for odex file '" << odex_file_location << "', skipping";
+    LOG(ERROR) << "Unable to determine isa for file '" << file_location << "', skipping";
     return false;
   }
   output_file_location += isa;
   output_file_location += '/';
-  return OdexFilenameToCacheFile(odex_file_location, output_file_location);
+  return SystemBFilenameToCacheFile(file_location, output_file_location);
 }
 
 // This program is used to determine where in the /data directory the runtime will search for an
@@ -115,9 +121,9 @@ int main(int argc, char *argv[]) {
     LOG(ERROR) << "usage: preopt2cachename preopt-location";
     return 2;
   }
-  std::string odex_file_location(argv[1]);
+  std::string file_location(argv[1]);
   std::string output_file_location(kDalvikCacheDir);
-  if (!OdexToCacheFile(odex_file_location, output_file_location)) {
+  if (!SystemBFileToCacheFile(file_location, output_file_location)) {
     return 1;
   } else {
     std::cout << output_file_location;
index e9d08dc..dbb4efc 100644 (file)
@@ -163,6 +163,10 @@ static bool puncture_fs (const char * const path, const u64 total_size,
                 (int) (100.0 * starting_max / total_size));
         hole_max = get_random_num(starting_max, ending_max);
 
+       do {
+               hole_max = get_random_num(starting_max, ending_max);
+       } while (hole_max == starting_max);
+
         create_unique_file(stay_dir,
                            hole_max - starting_max,
                            file_id++,
index 16e0e5c..a1715ec 100644 (file)
@@ -199,13 +199,24 @@ bool FindAHotpluggableCpu(int* hotpluggable_cpu) {
 struct CpuToggleThreadArg {
   int toggle_cpu;
   std::atomic<bool> end_flag;
+  std::atomic<bool> cpu_hotplug_failed;
+
+  CpuToggleThreadArg(int cpu)
+      : toggle_cpu(cpu), end_flag(false), cpu_hotplug_failed(false) {
+  }
 };
 
 static void CpuToggleThread(CpuToggleThreadArg* arg) {
   while (!arg->end_flag) {
-    CHECK(SetCpuOnline(arg->toggle_cpu, true));
+    if (!SetCpuOnline(arg->toggle_cpu, true)) {
+      arg->cpu_hotplug_failed = true;
+      break;
+    }
     std::this_thread::sleep_for(cpu_hotplug_interval);
-    CHECK(SetCpuOnline(arg->toggle_cpu, false));
+    if (!SetCpuOnline(arg->toggle_cpu, false)) {
+      arg->cpu_hotplug_failed = true;
+      break;
+    }
     std::this_thread::sleep_for(cpu_hotplug_interval);
   }
 }
@@ -223,9 +234,7 @@ TEST(cpu_offline, offline_while_recording) {
   if (!FindAHotpluggableCpu(&test_cpu)) {
     return;
   }
-  CpuToggleThreadArg cpu_toggle_arg;
-  cpu_toggle_arg.toggle_cpu = test_cpu;
-  cpu_toggle_arg.end_flag = false;
+  CpuToggleThreadArg cpu_toggle_arg(test_cpu);
   std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
 
   std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
@@ -240,7 +249,7 @@ TEST(cpu_offline, offline_while_recording) {
   auto report_step = std::chrono::seconds(15);
   size_t iterations = 0;
 
-  while (cur_time < end_time) {
+  while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
     if (cur_time + report_step < std::chrono::steady_clock::now()) {
       // Report test time.
       auto diff = std::chrono::duration_cast<std::chrono::seconds>(
@@ -261,6 +270,9 @@ TEST(cpu_offline, offline_while_recording) {
       GTEST_LOG_(INFO) << "Test offline while recording for " << iterations << " times.";
     }
   }
+  if (cpu_toggle_arg.cpu_hotplug_failed) {
+    GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
+  }
   cpu_toggle_arg.end_flag = true;
   cpu_toggle_thread.join();
 }
@@ -278,9 +290,7 @@ TEST(cpu_offline, offline_while_ioctl_enable) {
   if (!FindAHotpluggableCpu(&test_cpu)) {
     return;
   }
-  CpuToggleThreadArg cpu_toggle_arg;
-  cpu_toggle_arg.toggle_cpu = test_cpu;
-  cpu_toggle_arg.end_flag = false;
+  CpuToggleThreadArg cpu_toggle_arg(test_cpu);
   std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
 
   std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
@@ -295,7 +305,7 @@ TEST(cpu_offline, offline_while_ioctl_enable) {
   auto report_step = std::chrono::seconds(15);
   size_t iterations = 0;
 
-  while (cur_time < end_time) {
+  while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
     if (cur_time + report_step < std::chrono::steady_clock::now()) {
       // Report test time.
       auto diff = std::chrono::duration_cast<std::chrono::seconds>(
@@ -319,6 +329,9 @@ TEST(cpu_offline, offline_while_ioctl_enable) {
       GTEST_LOG_(INFO) << "Test offline while ioctl(PERF_EVENT_IOC_ENABLE) for " << iterations << " times.";
     }
   }
+  if (cpu_toggle_arg.cpu_hotplug_failed) {
+    GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
+  }
   cpu_toggle_arg.end_flag = true;
   cpu_toggle_thread.join();
 }
@@ -350,9 +363,7 @@ TEST(cpu_offline, offline_while_user_process_profiling) {
   if (!FindAHotpluggableCpu(&test_cpu)) {
     return;
   }
-  CpuToggleThreadArg cpu_toggle_arg;
-  cpu_toggle_arg.toggle_cpu = test_cpu;
-  cpu_toggle_arg.end_flag = false;
+  CpuToggleThreadArg cpu_toggle_arg(test_cpu);
   std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
 
   // Start cpu spinner.
@@ -378,7 +389,7 @@ TEST(cpu_offline, offline_while_user_process_profiling) {
   auto report_step = std::chrono::seconds(15);
   size_t iterations = 0;
 
-  while (cur_time < end_time) {
+  while (cur_time < end_time && !cpu_toggle_arg.cpu_hotplug_failed) {
     if (cur_time + report_step < std::chrono::steady_clock::now()) {
       auto diff = std::chrono::duration_cast<std::chrono::seconds>(
           std::chrono::steady_clock::now() - start_time);
@@ -403,13 +414,17 @@ TEST(cpu_offline, offline_while_user_process_profiling) {
       GTEST_LOG_(INFO) << "Test offline while user process profiling for " << iterations << " times.";
     }
   }
+  if (cpu_toggle_arg.cpu_hotplug_failed) {
+    GTEST_LOG_(INFO) << "Test ends because of cpu hotplug failure.";
+  }
   cpu_toggle_arg.end_flag = true;
   cpu_toggle_thread.join();
   cpu_spin_arg.end_flag = true;
   cpu_spin_thread.join();
   // Check if the cpu-cycle event is still available on test_cpu.
-  ASSERT_TRUE(SetCpuOnline(test_cpu, true));
-  ASSERT_TRUE(EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, true) != nullptr);
+  if (SetCpuOnline(test_cpu, true)) {
+    ASSERT_TRUE(EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, true) != nullptr);
+  }
 }
 
 // http://b/19863147.
@@ -433,10 +448,14 @@ TEST(cpu_offline, offline_while_recording_on_another_cpu) {
   const size_t TEST_ITERATION_COUNT = 10u;
   for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
     int record_cpu = 0;
-    ASSERT_TRUE(SetCpuOnline(test_cpu, true));
+    if (!SetCpuOnline(test_cpu, true)) {
+      break;
+    }
     std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr);
     ASSERT_TRUE(event_fd != nullptr);
-    ASSERT_TRUE(SetCpuOnline(test_cpu, false));
+    if (!SetCpuOnline(test_cpu, false)) {
+      break;
+    }
     event_fd = nullptr;
     event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr);
     ASSERT_TRUE(event_fd != nullptr);
diff --git a/tests/bootloader/Android.mk b/tests/bootloader/Android.mk
new file mode 100644 (file)
index 0000000..ab311fd
--- /dev/null
@@ -0,0 +1,25 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build a module that has all of the python files as its LOCAL_PICKUP_FILES.
+# Since no action needs to be taken to compile the python source, just
+# use BUILD_PHONY_PACKAGE to give us a target to execute.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootloader_unit_test
+LOCAL_MODULE_TAGS := tests
+
+bootloader_py_files := $(call find-subdir-files, *.py)
+
+bootloader_zip_prefix := $(TARGET_OUT_DATA)/py_bootloader
+bootloader_zip_path := $(bootloader_zip_prefix)/nativetest/py_bootloader
+
+GEN := $(addprefix $(bootloader_zip_path)/, $(bootloader_py_files))
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN) : $(bootloader_zip_path)/% : $(LOCAL_PATH)/%
+       $(transform-generated-source)
+
+LOCAL_PICKUP_FILES := $(bootloader_zip_prefix)/nativetest
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GEN)
+
+include $(BUILD_PHONY_PACKAGE)
index 132efd3..9874ffd 100644 (file)
@@ -2,7 +2,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= icache_main.c icache.S icache2.S
+LOCAL_SRC_FILES:= icache_main.cpp Profiler.cpp icache.S
 
 LOCAL_SHARED_LIBRARIES := libc
 
@@ -12,4 +12,6 @@ LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE_TARGET_ARCH := arm
 
+LOCAL_CFLAGS += -Wall -Werror
+
 include $(BUILD_EXECUTABLE)
diff --git a/tests/icachetest/Profiler.cpp b/tests/icachetest/Profiler.cpp
new file mode 100644 (file)
index 0000000..792cf43
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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 "Profiler.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <iostream>
+
+#if defined(__linux__)
+
+#include <sys/syscall.h>
+
+#ifdef __ARM_ARCH
+    enum ARMv8PmuPerfTypes{
+        // Common micro-architecture events
+        ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL    = 0x01,
+        ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS    = 0x14,
+        ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS     = 0x16,
+        ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL     = 0x17,
+        ARMV8_PMUV3_PERFCTR_L2_CACHE_WB         = 0x18,
+    };
+#endif
+
+static int perf_event_open(struct perf_event_attr* hw_event, pid_t pid,
+        int cpu, int group_fd, unsigned long flags) {
+    return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+}
+
+#endif // __linux__
+
+namespace utils {
+
+Profiler& Profiler::get() noexcept {
+    static Profiler sProfiler;
+    return sProfiler;
+}
+
+Profiler::Profiler() noexcept {
+    std::uninitialized_fill(mCountersFd.begin(), mCountersFd.end(), -1);
+    Profiler::resetEvents(EV_CPU_CYCLES | EV_L1D_RATES | EV_BPU_RATES);
+}
+
+Profiler::~Profiler() noexcept {
+    for (int fd : mCountersFd) {
+        if (fd >= 0) {
+            close(fd);
+        }
+    }
+}
+
+uint32_t Profiler::resetEvents(uint32_t eventMask) noexcept {
+    // close all counters
+    for (int& fd : mCountersFd) {
+        if (fd >= 0) {
+            close(fd);
+            fd = -1;
+        }
+    }
+    mEnabledEvents = 0;
+
+#if defined(__linux__)
+
+    struct perf_event_attr pe;
+    memset(&pe, 0, sizeof(struct perf_event_attr));
+    pe.type = PERF_TYPE_HARDWARE;
+    pe.size = sizeof(struct perf_event_attr);
+    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
+    pe.disabled = 1;
+    pe.exclude_kernel = 1;
+    pe.exclude_hv = 1;
+    pe.read_format = PERF_FORMAT_GROUP |
+                     PERF_FORMAT_ID |
+                     PERF_FORMAT_TOTAL_TIME_ENABLED |
+                     PERF_FORMAT_TOTAL_TIME_RUNNING;
+
+    uint8_t count = 0;
+    int fd = perf_event_open(&pe, 0, -1, -1, 0);
+    if (fd >= 0) {
+        const int groupFd = fd;
+        mIds[INSTRUCTIONS] = count++;
+        mCountersFd[INSTRUCTIONS] = fd;
+
+        pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
+
+        if (eventMask & EV_CPU_CYCLES) {
+            pe.type = PERF_TYPE_HARDWARE;
+            pe.config = PERF_COUNT_HW_CPU_CYCLES;
+            mCountersFd[CPU_CYCLES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[CPU_CYCLES] > 0) {
+                mIds[CPU_CYCLES] = count++;
+                mEnabledEvents |= EV_CPU_CYCLES;
+            }
+        }
+
+        if (eventMask & EV_L1D_REFS) {
+            pe.type = PERF_TYPE_HARDWARE;
+            pe.config = PERF_COUNT_HW_CACHE_REFERENCES;
+            mCountersFd[DCACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[DCACHE_REFS] > 0) {
+                mIds[DCACHE_REFS] = count++;
+                mEnabledEvents |= EV_L1D_REFS;
+            }
+        }
+
+        if (eventMask & EV_L1D_MISSES) {
+            pe.type = PERF_TYPE_HARDWARE;
+            pe.config = PERF_COUNT_HW_CACHE_MISSES;
+            mCountersFd[DCACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[DCACHE_MISSES] > 0) {
+                mIds[DCACHE_MISSES] = count++;
+                mEnabledEvents |= EV_L1D_MISSES;
+            }
+        }
+    
+        if (eventMask & EV_BPU_REFS) {
+            pe.type = PERF_TYPE_HARDWARE;
+            pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
+            mCountersFd[BRANCHES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[BRANCHES] > 0) {
+                mIds[BRANCHES] = count++;
+                mEnabledEvents |= EV_BPU_REFS;
+            }
+        }
+    
+        if (eventMask & EV_BPU_MISSES) {
+            pe.type = PERF_TYPE_HARDWARE;
+            pe.config = PERF_COUNT_HW_BRANCH_MISSES;
+            mCountersFd[BRANCH_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[BRANCH_MISSES] > 0) {
+                mIds[BRANCH_MISSES] = count++;
+                mEnabledEvents |= EV_BPU_MISSES;
+            }
+        }
+    
+#ifdef __ARM_ARCH
+        if (eventMask & EV_L1I_REFS) {
+            pe.type = PERF_TYPE_RAW;
+            pe.config = ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS;
+            mCountersFd[ICACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[ICACHE_REFS] > 0) {
+                mIds[ICACHE_REFS] = count++;
+                mEnabledEvents |= EV_L1I_REFS;
+            }
+        }
+
+        if (eventMask & EV_L1I_MISSES) {
+            pe.type = PERF_TYPE_RAW;
+            pe.config = ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL;
+            mCountersFd[ICACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[ICACHE_MISSES] > 0) {
+                mIds[ICACHE_MISSES] = count++;
+                mEnabledEvents |= EV_L1I_MISSES;
+            }
+        }
+#else
+        if (eventMask & EV_L1I_REFS) {
+            pe.type = PERF_TYPE_HW_CACHE;
+            pe.config = PERF_COUNT_HW_CACHE_L1I | 
+                (PERF_COUNT_HW_CACHE_OP_READ<<8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS<<16);
+            mCountersFd[ICACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[ICACHE_REFS] > 0) {
+                mIds[ICACHE_REFS] = count++;
+                mEnabledEvents |= EV_L1I_REFS;
+            }
+        }
+
+        if (eventMask & EV_L1I_MISSES) {
+            pe.type = PERF_TYPE_HW_CACHE;
+            pe.config = PERF_COUNT_HW_CACHE_L1I | 
+                (PERF_COUNT_HW_CACHE_OP_READ<<8) | (PERF_COUNT_HW_CACHE_RESULT_MISS<<16);
+            mCountersFd[ICACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
+            if (mCountersFd[ICACHE_MISSES] > 0) {
+                mIds[ICACHE_MISSES] = count++;
+                mEnabledEvents |= EV_L1I_MISSES;
+            }
+        }
+#endif
+    }
+#endif // __linux__
+    return mEnabledEvents;
+}
+
+} // namespace utils
diff --git a/tests/icachetest/Profiler.h b/tests/icachetest/Profiler.h
new file mode 100644 (file)
index 0000000..a36cab3
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+#ifndef TNT_UTILS_PROFILER_H
+#define TNT_UTILS_PROFILER_H
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <array>
+#include <chrono>
+
+#if defined(__linux__)
+#   include <unistd.h>
+#   include <sys/ioctl.h>
+#   include <linux/perf_event.h>
+#endif
+
+namespace utils {
+
+class Profiler {
+    enum {
+        INSTRUCTIONS    = 0,   // must be zero
+        CPU_CYCLES      = 1,
+        DCACHE_REFS     = 2,
+        DCACHE_MISSES   = 3,
+        BRANCHES        = 4,
+        BRANCH_MISSES   = 5,
+        ICACHE_REFS     = 6,
+        ICACHE_MISSES   = 7,
+
+        // Must be last one
+        EVENT_COUNT
+    };
+
+public:
+
+    enum {
+        EV_CPU_CYCLES = 1 << CPU_CYCLES,
+        EV_L1D_REFS   = 1 << DCACHE_REFS,
+        EV_L1D_MISSES = 1 << DCACHE_MISSES,
+        EV_BPU_REFS   = 1 << BRANCHES,
+        EV_BPU_MISSES = 1 << BRANCH_MISSES,
+        EV_L1I_REFS   = 1 << ICACHE_REFS,
+        EV_L1I_MISSES = 1 << ICACHE_MISSES,
+        // helpers
+        EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES,
+        EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES,
+        EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES,
+    };
+
+    static Profiler& get() noexcept;
+
+
+    Profiler(const Profiler& rhs) = delete;
+    Profiler(Profiler&& rhs) = delete;
+    Profiler& operator=(const Profiler& rhs) = delete;
+    Profiler& operator=(Profiler&& rhs) = delete;
+
+    // selects which events are enabled. 
+    // By Default: EV_CPU_CYCLES | EV_L1D_RATES | EV_BPU_RATES
+    uint32_t resetEvents(uint32_t eventMask) noexcept;
+
+    uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; }
+
+    // could return false if performance counters are not supported/enabled
+    bool isValid() const { return mCountersFd[0] >= 0; }
+
+    class Counters {
+        friend class Profiler;
+        uint64_t nr;
+        uint64_t time_enabled;
+        uint64_t time_running;
+        struct {
+            uint64_t value;
+            uint64_t id;
+        } counters[Profiler::EVENT_COUNT];
+
+        friend Counters operator-(Counters lhs, const Counters& rhs) noexcept {
+            lhs.nr -= rhs.nr;
+            lhs.time_enabled -= rhs.time_enabled;
+            lhs.time_running -= rhs.time_running;
+            for (size_t i=0 ; i<EVENT_COUNT ; ++i) {
+                lhs.counters[i].value -= rhs.counters[i].value;
+            }
+            return lhs;
+        }
+
+    public:
+        uint64_t getInstructions() const        { return counters[INSTRUCTIONS].value; }
+        uint64_t getCpuCycles() const           { return counters[CPU_CYCLES].value; }
+        uint64_t getL1DReferences() const       { return counters[DCACHE_REFS].value; }
+        uint64_t getL1DMisses() const           { return counters[DCACHE_MISSES].value; }
+        uint64_t getL1IReferences() const       { return counters[ICACHE_REFS].value; }
+        uint64_t getL1IMisses() const           { return counters[ICACHE_MISSES].value; }
+        uint64_t getBranchInstructions() const  { return counters[BRANCHES].value; }
+        uint64_t getBranchMisses() const        { return counters[BRANCH_MISSES].value; }
+
+        std::chrono::duration<uint64_t, std::nano> getWallTime() const {
+            return std::chrono::duration<uint64_t, std::nano>(time_enabled);
+        }
+
+        std::chrono::duration<uint64_t, std::nano> getRunningTime() const {
+            return std::chrono::duration<uint64_t, std::nano>(time_running);
+        }
+
+        double getIPC() const noexcept {
+            uint64_t cpuCycles = getCpuCycles();
+            uint64_t instructions = getInstructions();
+            return double(instructions) / double(cpuCycles);
+        }
+
+        double getCPI() const noexcept {
+            uint64_t cpuCycles = getCpuCycles();
+            uint64_t instructions = getInstructions();
+            return double(cpuCycles) / double(instructions);
+        }
+
+        double getL1DMissRate() const noexcept {
+            uint64_t cacheReferences = getL1DReferences();
+            uint64_t cacheMisses = getL1DMisses();
+            return double(cacheMisses) / double(cacheReferences);
+        }
+
+        double getL1DHitRate() const noexcept {
+            return 1.0 - getL1DMissRate();
+        }
+
+        double getL1IMissRate() const noexcept {
+            uint64_t cacheReferences = getL1IReferences();
+            uint64_t cacheMisses = getL1IMisses();
+            return double(cacheMisses) / double(cacheReferences);
+        }
+
+        double getL1IHitRate() const noexcept {
+            return 1.0 - getL1IMissRate();
+        }
+
+        double getBranchMissRate() const noexcept {
+            uint64_t branchReferences = getBranchInstructions();
+            uint64_t branchMisses = getBranchMisses();
+            return double(branchMisses) / double(branchReferences);
+        }
+
+        double getBranchHitRate() const noexcept {
+            return 1.0 - getBranchMissRate();
+        }
+
+        double getMPKI(uint64_t misses) const noexcept {
+            return (misses * 1000.0) / getInstructions();
+        }
+
+    };
+
+#if defined(__linux__)
+
+    void reset() noexcept {
+        int fd = mCountersFd[0];
+        ioctl(fd, PERF_EVENT_IOC_RESET,  PERF_IOC_FLAG_GROUP);
+    }
+
+    void start() noexcept {
+        int fd = mCountersFd[0];
+        ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
+    }
+
+    void stop() noexcept {
+        int fd = mCountersFd[0];
+        ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
+    }
+
+    void readCounters(Counters* outCounters) noexcept {
+        Counters counters;
+        ssize_t n = read(mCountersFd[0], &counters, sizeof(Counters));
+        memset(outCounters, 0, sizeof(Counters));
+        if (n > 0) {
+            outCounters->nr = counters.nr;
+            outCounters->time_enabled = counters.time_enabled;
+            outCounters->time_running = counters.time_running;
+            for (size_t i=0 ; i<size_t(EVENT_COUNT) ; i++) {
+                if (mCountersFd[i] >= 0) {
+                    outCounters->counters[i] = counters.counters[mIds[i]];
+                }
+            }
+        }
+    }
+
+#else // !__linux__
+
+    void reset() noexcept { }
+    void start() noexcept { }
+    void stop() noexcept { }
+    void readCounters(Counters* counters) noexcept { }
+
+#endif // __linux__
+
+    bool hasBranchRates() const noexcept {
+        return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0);
+    }
+
+    bool hasICacheRates() const noexcept {
+        return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0);
+    }
+
+private:
+    Profiler() noexcept;
+    ~Profiler() noexcept;
+
+    std::array<uint8_t, EVENT_COUNT> mIds;
+    std::array<int, EVENT_COUNT> mCountersFd;
+    uint32_t mEnabledEvents = 0;
+};
+
+} // namespace utils
+
+#endif // TNT_UTILS_PROFILER_H
index fbe8fa7..e82895d 100644 (file)
         mov     r0, r0                 ; \
         mov     r0, r0                 ; \
         mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
+        mov     r0, r0                 ; \
         beq     end_loop               ; \
         mov     r0, r0                 ; \
 
@@ -37,6 +45,14 @@ icache_test:
         mov     r0, r0
         mov     r0, r0
         mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
+        mov     r0, r0
 
 end_loop:
                subs      r0, r0, r1
diff --git a/tests/icachetest/icache2.S b/tests/icachetest/icache2.S
deleted file mode 100644 (file)
index 2a204ce..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- *  icache.s
- *  
- *
- *  Copyright 2005 The Android Open Source Project
- *
- */
-
-    .text
-    .align
-    
-    .global icache_test2
-    .type icache_test2, %function
-
-#define LOOP                             \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ; \
-        mov     r0, r0                 ;
-
-
-    /*
-     * r0 = loop_count
-     * r1 = step
-     * r2 = mask
-     */
-
-icache_test2:
-end_loop:
-        
-        /* each loop iteration is one cache line 
-           repeat this block 2048 times... */
-
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-        LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP LOOP 
-    
-        subs    r0, r0, #1
-        bgt     end_loop
-        bx      lr
-
-        
diff --git a/tests/icachetest/icache_main.c b/tests/icachetest/icache_main.c
deleted file mode 100644 (file)
index 93f36d4..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <stdio.h>
-#include <sys/time.h>
-
-extern void icache_test(long count, long step);
-extern void icache_test2(long count);
-
-int main() 
-{
-    printf("[bytes]\t[us]\n");
-
-    struct timeval now, tm;
-    long long t;
-    long MBs;
-    long i;
-    long step = 32;
-    for (i=0 ; step<=2048 ; i++, step+=32) 
-    {
-        long value;
-        gettimeofday(&now, 0);
-        icache_test(0x800000L, step);
-        gettimeofday(&tm, 0);
-        t = (tm.tv_sec*1000000LL+tm.tv_usec) - (now.tv_sec*1000000LL+now.tv_usec);
-        printf("%6ld\t%lld\n", step*32, t);
-    }
-
-    gettimeofday(&now, 0);
-    icache_test2(0x800000L / 2048);
-    gettimeofday(&tm, 0);
-    t = (tm.tv_sec*1000000LL+tm.tv_usec) - (now.tv_sec*1000000LL+now.tv_usec);
-    MBs = (8388608LL*32*1000000) / (t * (1024*1024));
-    printf("\n%6lld us\t%ld MB/s\n", t, MBs);
-    
-    return 0;
-}
diff --git a/tests/icachetest/icache_main.cpp b/tests/icachetest/icache_main.cpp
new file mode 100644 (file)
index 0000000..d5aeda0
--- /dev/null
@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#include <thread>
+#include <iostream>
+#include <iomanip>
+
+#include <sched.h>
+
+#include "Profiler.h"
+
+extern "C" void icache_test(long count, long step);
+
+static constexpr size_t MAX_CODE_SIZE = 128*1024;
+static constexpr size_t CACHE_LINE_SIZE = 64;
+static constexpr size_t MAX_ITERATIONS_COUNT = MAX_CODE_SIZE / CACHE_LINE_SIZE;
+static constexpr size_t REPETITIONS = 0x800000L;
+
+
+using namespace utils;
+
+static cpu_set_t g_cpu_set;
+
+static void printUsage(char* name) {
+    std::string exec_name(name);
+    std::string usage(
+            "ICACHE is a command-line tool for testing the L1 instruction cache performance.\n"
+            "(Make sure security.perf_harden is set to 0)\n\n"
+            "Usages:\n"
+            "    ICACHE [options]\n"
+            "\n"
+            "Options:\n"
+            "   --help, -h\n"
+            "       print this message\n\n"
+            "   --affinity=N, -a N\n"
+            "       Specify which CPU the test should run on.\n\n"
+    );
+    const std::string from("ICACHE");
+    for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
+         usage.replace(pos, from.length(), exec_name);
+    }
+    printf("%s", usage.c_str());
+}
+
+static int handleCommandLineArgments(int argc, char* argv[]) {
+    static constexpr const char* OPTSTR = "ha:";
+    static const struct option OPTIONS[] = {
+            { "help",                 no_argument, 0, 'h' },
+            { "affinity",       required_argument, 0, 'a' },
+            { 0, 0, 0, 0 }  // termination of the option list
+    };
+    int opt;
+    int option_index = 0;
+    while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
+        std::string arg(optarg ? optarg : "");
+        switch (opt) {
+            default:
+            case 'h':
+                printUsage(argv[0]);
+                exit(0);
+                break;
+            case 'a':
+                size_t cpu = std::stoi(arg);
+                if (cpu < std::thread::hardware_concurrency()) {
+                    CPU_SET(cpu, &g_cpu_set);
+                } else {
+                    std::cerr << "N must be < " << std::thread::hardware_concurrency() << std::endl;
+                    exit(0);
+                }
+                break;
+        }
+    }
+    return optind;
+}
+
+int main(int argc, char* argv[]) {
+    CPU_ZERO(&g_cpu_set);
+
+    [[maybe_unused]] int option_index = handleCommandLineArgments(argc, argv);
+    [[maybe_unused]] int num_args = argc - option_index;
+
+    if (CPU_COUNT(&g_cpu_set)) {
+        sched_setaffinity(gettid(), sizeof(g_cpu_set), &g_cpu_set);
+    }
+
+    Profiler& profiler = Profiler::get();
+    profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_L1I_RATES);
+
+    if (!profiler.isValid()) {
+        fprintf(stderr, "performance counters not enabled. try \"setprop security.perf_harden 0\"\n");
+        exit(0);
+    }
+
+    size_t const stepInBytes = 1024;    // 1 KiB steps
+    size_t const step = stepInBytes / CACHE_LINE_SIZE;
+
+    std::cout << std::fixed << std::setprecision(2);
+
+    printf("[KiB]\t[cyc]\t[refs]\t[MPKI]\t[ns]\n");
+
+    Profiler::Counters counters;
+
+    for (size_t i=step ; i <= MAX_ITERATIONS_COUNT ; i += step) {
+        profiler.reset();
+
+        auto now = std::chrono::steady_clock::now();
+        profiler.start();
+        icache_test(REPETITIONS, i);
+        profiler.stop();
+        auto duration = std::chrono::steady_clock::now() - now;
+
+        profiler.readCounters(&counters);
+
+        std::cout << ((i*CACHE_LINE_SIZE)/1024) << "\t"
+            << counters.getCpuCycles()/double(REPETITIONS) << "\t"
+            << counters.getL1IReferences()/double(REPETITIONS) << "\t"
+            << counters.getMPKI(counters.getL1IMisses()) << "\t"
+            << duration.count()/double(REPETITIONS) << "\t"
+            << std::endl;
+    }
+
+    return 0;
+}
index af68c05..0c87107 100644 (file)
@@ -15,7 +15,6 @@ test_c_flags := \
 
 # Required Tests
 cts_src_files := \
-    aslr_test.cpp \
     logger_test.cpp \
     multicast_test.cpp \
     nfs_test.cpp \
@@ -23,8 +22,10 @@ cts_src_files := \
     sysvipc_test.cpp \
 
 # Required plus Recommended Tests
+# TODO: move aslr_test.cpp back to cts_src_files b/36888825
 test_src_files := \
     $(cts_src_files) \
+    aslr_test.cpp \
     aslr_rec_test.cpp \
     mmc_max_speed_test.cpp \
 
index 4fe3192..1e50e0a 100644 (file)
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Kernel Config test cases">
+    <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsKernelConfigTestCases->/data/local/tmp/CtsKernelConfigTestCases" />
index c93c52b..7efa650 100644 (file)
@@ -132,7 +132,7 @@ void usage()
            "  -s --size:        Size in kbytes of the data.\n"
            "  -S --chunk-size:  Size of a chunk. Default to size ie 1 chunk.\n"
            "                    Data will be written/read using that chunk size.\n"
-           "  -D --depth:       Depth of directory tree to create for traversal.\n",
+           "  -D --depth:       Depth of directory tree to create for traversal.\n"
            "  -i --iterations:  Number of time a process should carry its task.\n"
            "  -p --procnb:      Number of processes to use.\n"
            "  -d --dump:        Print the raw timing on stdout.\n"
index 3d3aeb8..c527b54 100755 (executable)
@@ -99,7 +99,7 @@ function hwuiOutputParser {
        # Number Slow bitmap uploads: 12
        # Number Slow draw: 89
        # use with "stdbuf -o0 " to disable pipe buffering
-       # stdbuf -o0 adb shell /data/hwuitest shadowgrid2 400 | stdbuf -o0 ./hwuitestfilter.sh  | tee t.csv
+       # stdbuf -o0 adb shell /data/local/tmp/hwuimacro shadowgrid2 400 | stdbuf -o0 ./hwuitestfilter.sh  | tee t.csv
        sed -e 's/ns//' -e 's/[\(\)%]/ /g' | awk '
        BEGIN { startTime=0; lastTime=0; }
        /^Stats since:/ {
index 39f7b11..fd5d825 100755 (executable)
@@ -94,16 +94,16 @@ suntempledir=${CMDDIR}/suntemple
 
 case $DEVICE in
 (shamu|hammerhead)
-       HWUITEST=hwuitest
+       HWUIMACRO=hwuimacro
        onSwipe="700 1847 700 400 50"
        ;;
 (*)
-       HWUITEST=hwuitest64
+       HWUIMACRO=hwuimacro64
        onSwipe="500 1200 500 550 150"
        ;;
 esac
 
-scripts="defs.sh systemapps.sh recentfling.sh youtube.sh chromefling.sh $HWUITEST"
+scripts="defs.sh systemapps.sh recentfling.sh youtube.sh chromefling.sh"
 
 if ! $MONSOON >/dev/null 2>&1; then
        echo $MONSOON must be in your PATH >&2
@@ -253,6 +253,7 @@ fi
 
 echo Copying $scripts to device $devdir...
 copy_files
+adb shell ln -s /data/benchmarktest/hwuimacro/$HWUIMACRO $devdir/$HWUIMACRO
 tests=""
 
 # measure background power
@@ -332,9 +333,9 @@ fi
 if [ $shadowgrid2Time -gt 0 ]; then
        airplane_mode on
        echo $(date) Test 4 : shadowgrid2 for $shadowgrid2Time minutes
-       start_job "./$HWUITEST shadowgrid2 100000"
+       start_job "./$HWUIMACRO --onscreen shadowgrid2 100000"
        run_test shadowgrid2 $shadowgrid2Time
-       cleanup_job shadowgrid2 $HWUITEST
+       cleanup_job shadowgrid2 $HWUIMACRO
        airplane_mode off
        date
        tests="$tests shadowgrid2"