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,
* is_heavy_loaded() will return false.
*/
ALOGE("Error in getting cpu status. Skipping this check.");
+ fclose(fp);
return;
}
/*
* 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) {
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");
}
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);
*/
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) {
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);
# 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
}
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
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
#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
if [ "$1" = "-s" ]; then
MKE2FS_EXTENDED_OPTS+="android_sparse"
shift
+else
+ E2FSDROID_OPTS+="-e"
fi
if [ $# -lt 5 ]; 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
# 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
$MAKE_EXT4FS_CMD
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
--- /dev/null
+# 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
+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
+include $(BUILD_HOST_EXECUTABLE)
+
+
+
--- /dev/null
+IOshark is a repeatable application workload storage benchmark. You
+can find more documentation on IOshark at :
+https://docs.google.com/a/google.com/document/d/1Bhq7iNPVc_JzwRrkmZqcPjMvWgpHX0r3Ncq-ZsRNOBA/edit?usp=sharing
+
+The short summary of what IOshark is : IOshark has 2 components, one
+is a strace compiler that takes straces fed into it and compiles this
+into bytecodes (stored in *.wl files). The compiler runs on a Linux
+host. The second component (which runs on the device) is the tester
+that takes as input the bytecode files (*.wl files) and executes them
+on the device.
+
+How to Run :
+----------
+- First collect straces and compile these into bytecodes. The wrapper
+script provided (collect-straces.sh) collects straces, ships them to
+the host where the script runs, compiles and packages up the bytecode
+files into a wl.tar file.
+- Ship the wl.tar file and the iostark_bench binaries to the target
+device (on /data/local/tmp say). Explode the tarfile.
+- Run the tester. "ioshark_bench *.wl" runs the test with default
+options. Supported ioshark_bench options :
+-d : Preserve the delays between successive filesystem syscalls as
+seen in the original straces.
+-n <N> : Run for N iterations
+-t <N> : Limit to N threads. By default (without this option), IOshark
+will launch as many threads as there are input files, so 1 thread/file.
+-v : verbose. Chatty mode.
--- /dev/null
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+# # Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#!/bin/sh
+
+# When signal is received, the stracer will get killed
+# Call this (just to make sure anyway)
+kill_strace() {
+ ps_line=`ps -ef | grep strace | grep adb `
+ if [ $? == 0 ]; then
+ echo Killing `echo $ps_line | awk '{s = ""; for (i=8; i <= NF ; i++) s = s \
+$i " "; print s}' `
+ kill `echo $ps_line | awk '{print $2}' `
+ fi
+}
+
+catch_sigint()
+{
+ echo "signal INT received, killing streaming trace capture"
+ kill_strace
+}
+
+compile_tracefiles()
+{
+ for i in trace.*
+ do
+ if [ $i != trace.begin ] && [ $i != trace.tar ];
+ then
+ egrep '\/system\/|\/data\/|\/vendor\/' $i > bar
+# parse out /sys/devices/system/...
+ egrep -v '\/sys\/devices\/system\/' bar > bar0
+ mv bar0 bar
+ fgrep -v '= -1' bar > foo
+ mv foo bar
+ if [ -s bar ]
+ then
+ echo parsing $i
+ compile_ioshark bar $i.wl
+ rm -f bar
+ else
+ rm -f $i bar
+ fi
+ fi
+ done
+}
+
+# main() starts here
+
+adb root && adb wait-for-device
+
+#adb shell 'ps -ef' | grep zygote > zygote_pids
+adb shell 'ps' | grep zygote > zygote_pids
+
+fgrep -v grep zygote_pids > bar
+mv bar zygote_pids
+pid1=`grep -w zygote zygote_pids | awk '{print $2}' `
+pid2=`grep -w zygote64 zygote_pids | awk '{print $2}' `
+rm -f zygote_pids
+
+trap 'catch_sigint' INT
+
+echo "^C this script once you finish running your test"
+
+adb shell "date +%s > /data/local/tmp/trace.begin ; strace -p $pid1,$pid2 -o /data/local/tmp/trace -q -qq -f -ff -y -ttt -e trace=mmap2,read,write,pread64,pwrite64,fsync,fdatasync,openat,close,lseek,_llseek"
+
+# Remove any remnant tracefiles first
+rm -f trace.*
+
+# Get the tracefiles from the device
+adb shell 'cd /data/local/tmp ; tar cvf trace.tar trace.*'
+adb pull /data/local/tmp/trace.tar
+
+# Extract the tracefiles from the device
+rm -f *.wl
+tar xf trace.tar
+
+# Compile the tracefiles
+compile_tracefiles
+
+# tar up the .wl files just created
+tar zcf wl.tgz *.wl
+
+
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "ioshark.h"
+#include "compile_ioshark.h"
+
+char *progname;
+
+char in_buf[2048];
+
+struct flags_map_s {
+ char *flag_str;
+ int flag;
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+struct flags_map_s open_flags_map[] = {
+ { "O_RDONLY", O_RDONLY },
+ { "O_WRONLY", O_WRONLY },
+ { "O_RDWR", O_RDWR },
+ { "O_CREAT", O_CREAT },
+ { "O_SYNC", O_SYNC },
+ { "O_TRUNC", O_TRUNC },
+ { "O_EXCL", O_EXCL },
+ { "O_APPEND", O_APPEND },
+ { "O_NOATIME", O_NOATIME },
+ { "O_ASYNC", O_ASYNC },
+ { "O_CLOEXEC", O_CLOEXEC },
+ { "O_DIRECT", O_DIRECT },
+ { "O_DIRECTORY", O_DIRECTORY },
+ { "O_LARGEFILE", O_LARGEFILE },
+ { "O_NOCTTY", O_NOCTTY },
+ { "O_NOFOLLOW", O_NOFOLLOW },
+ { "O_NONBLOCK", O_NONBLOCK },
+ { "O_NDELAY", O_NDELAY },
+ { "O_PATH", O_PATH }
+};
+
+struct flags_map_s lseek_action_map[] = {
+ { "SEEK_SET", SEEK_SET },
+ { "SEEK_CUR", SEEK_CUR },
+ { "SEEK_END", SEEK_END }
+};
+
+struct flags_map_s fileop_map[] = {
+ { "lseek", IOSHARK_LSEEK },
+ { "_llseek", IOSHARK_LLSEEK },
+ { "pread64", IOSHARK_PREAD64 },
+ { "pwrite64", IOSHARK_PWRITE64 },
+ { "read", IOSHARK_READ },
+ { "write", IOSHARK_WRITE },
+ { "mmap", IOSHARK_MMAP },
+ { "mmap2", IOSHARK_MMAP2 },
+ { "openat", IOSHARK_OPEN },
+ { "fsync", IOSHARK_FSYNC },
+ { "fdatasync", IOSHARK_FDATASYNC },
+ { "close", IOSHARK_CLOSE }
+};
+
+struct in_mem_file_op {
+ struct ioshark_file_operation disk_file_op;
+ struct in_mem_file_op *next;
+};
+
+struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL;
+
+void usage(void)
+{
+ fprintf(stderr, "%s in_file out_file\n", progname);
+}
+
+u_int64_t
+get_delta_ts(char *buf, struct timeval *start_tv)
+{
+ struct timeval op_tv, tv_res;
+
+ sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec);
+ timersub(&op_tv, start_tv, &tv_res);
+ return (tv_res.tv_usec + (tv_res.tv_sec * 1000000));
+}
+
+void
+get_pathname(char *buf, char *pathname, enum file_op file_op)
+{
+ char *s, *s2, save;
+
+ if (file_op == IOSHARK_OPEN)
+ s = strchr(buf, '"');
+ else
+ s = strchr(buf, '<');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 1;
+ if (file_op == IOSHARK_OPEN)
+ s2 = strchr(s, '"');
+ else
+ s2 = strchr(s, '>');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ save = *s2;
+ *s2 = '\0';
+ strcpy(pathname, s);
+ *s2 = save;
+}
+
+void
+get_syscall(char *buf, char *syscall)
+{
+ char *s, *s2;
+
+ s = strchr(buf, ' ');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 1;
+ s2 = strchr(s, '(');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(syscall, s);
+ *s2 = '(';
+}
+
+void
+get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len)
+{
+ char *s, *s1;
+ int i;
+ char protstr[128];
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ sscanf(s, "%ju", len);
+ s1 = strchr(s, ',');
+ if (s1 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s1 += 2;
+ s = strchr(s1, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s = '\0';
+ strcpy(protstr, s1);
+ *prot = 0;
+ if (strstr(protstr, "PROT_READ"))
+ *prot |= IOSHARK_PROT_READ;
+ if (strstr(protstr, "PROT_WRITE"))
+ *prot |= IOSHARK_PROT_WRITE;
+ *s = ',';
+ s += 2;
+ for (i = 0 ; i < 2 ; i++) {
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ }
+ sscanf(s, "%jx", offset);
+}
+
+void
+get_lseek_offset_action(char *buf, enum file_op op,
+ off_t *offset, char *action)
+{
+ char *s, *s2;
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ sscanf(s, "%ju", offset);
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ if (op == IOSHARK_LLSEEK) {
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ }
+ s2 = strchr(s, ')');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(action, s);
+ *s2 = ')';
+}
+
+void
+get_rw_len(char *buf,
+ u_int64_t *len)
+{
+ char *s_len;
+
+ s_len = strrchr(buf, ',');
+ if (s_len == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ sscanf(s_len + 2, "%ju", len);
+}
+
+void
+get_prw64_offset_len(char *buf,
+ off_t *offset,
+ u_int64_t *len)
+{
+ char *s_offset, *s_len;
+
+ s_offset = strrchr(buf, ',');
+ if (s_offset == NULL) {
+ fprintf(stderr, "%s: Malformed line 1: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s_offset = '\0';
+ s_len = strrchr(buf, ',');
+ if (s_len == NULL) {
+ fprintf(stderr, "%s: Malformed line 2: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s_offset = ',';
+ sscanf(s_len + 2, "%ju", len);
+ sscanf(s_offset + 2, "%ju", offset);
+}
+
+void
+get_openat_flags_mode(char *buf, char *flags, mode_t *mode)
+{
+ char *s, *s2, lookfor;
+
+ s = strchr(buf, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ s = strchr(s, ',');
+ if (s == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ s += 2;
+ if (strstr(s, "O_CREAT") == NULL)
+ lookfor = ')';
+ else
+ lookfor = ',';
+ s2 = strchr(s, lookfor);
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ strcpy(flags, s);
+ *s2 = lookfor;
+ if (strstr(s, "O_CREAT") != NULL) {
+ s = s2 + 2;
+ s2 = strchr(s, ')');
+ if (s2 == NULL) {
+ fprintf(stderr, "%s: Malformed line: %s\n",
+ __func__, buf);
+ exit(EXIT_FAILURE);
+ }
+ *s2 = '\0';
+ sscanf(s, "%o", mode);
+ *s2 = ')';
+ }
+}
+
+int
+lookup_map(char *s, struct flags_map_s *flags_map, int maplen)
+{
+ int found = 0, flag = 0;
+ int i;
+
+ while (isspace(*s))
+ s++;
+ for (i = 0 ; i < maplen ; i++) {
+ if (strcmp(flags_map[i].flag_str, s) == 0) {
+ flag = flags_map[i].flag;
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0) {
+ fprintf(stderr, "%s: Unknown syscall %s\n",
+ __func__, s);
+ exit(1);
+ }
+ return flag;
+}
+
+int
+map_open_flags(char *s)
+{
+ int flags = 0;
+ char *s1;
+
+ while ((s1 = strchr(s, '|'))) {
+ *s1 = '\0';
+ flags |= lookup_map(s, open_flags_map,
+ ARRAY_SIZE(open_flags_map));
+ *s1 = '|';
+ s = s1 + 1;
+ }
+ /* Last option */
+ flags |= lookup_map(s, open_flags_map,
+ ARRAY_SIZE(open_flags_map));
+ return flags;
+}
+
+int
+map_lseek_action(char *s)
+{
+ int flags = 0;
+ char *s1;
+
+ while ((s1 = strchr(s, '|'))) {
+ *s1 = '\0';
+ flags |= lookup_map(s, lseek_action_map,
+ ARRAY_SIZE(lseek_action_map));
+ *s1 = '|';
+ s = s1 + 1;
+ }
+ /* Last option */
+ flags |= lookup_map(s, lseek_action_map,
+ ARRAY_SIZE(lseek_action_map));
+ return flags;
+}
+
+enum file_op
+map_syscall(char *syscall)
+{
+ return lookup_map(syscall, fileop_map,
+ ARRAY_SIZE(fileop_map));
+}
+
+void
+get_start_time(struct timeval *tv)
+{
+ FILE *fp;
+
+ tv->tv_usec = 0;
+ fp = fopen("trace.begin", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open trace.begin\n",
+ __func__);
+ exit(EXIT_FAILURE);
+ }
+ if (fscanf(fp, "%lu", &tv->tv_sec) != 1) {
+ fprintf(stderr, "%s Can't read seconds from trace.begin\n",
+ __func__);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * For each tracefile, we first create in-memory structures, then once
+ * we've processed each tracefile completely, we write out the in-memory
+ * structures and free them.
+ */
+int main(int argc, char **argv)
+{
+ FILE *fp;
+ char path[512];
+ char syscall[512];
+ char lseek_action_str[512];
+ char *s;
+ char open_flags_str[64];
+ void *db_node;
+ int num_io_operations = 0;
+ struct ioshark_header header;
+ struct ioshark_file_operation *disk_file_op;
+ struct in_mem_file_op *in_mem_fop;
+ struct stat st;
+ char *infile, *outfile;
+ struct timeval start_time;
+
+ progname = argv[0];
+ if (argc != 3) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ infile = argv[1];
+ outfile = argv[2];
+ if (stat(infile, &st) < 0) {
+ fprintf(stderr, "%s Can't stat %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ if (st.st_size == 0) {
+ fprintf(stderr, "%s Empty file %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ get_start_time(&start_time);
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ while (fgets(in_buf, 2048, fp)) {
+ s = in_buf;
+ while (isspace(*s))
+ s++;
+ in_mem_fop = malloc(sizeof(struct in_mem_file_op));
+ disk_file_op = &in_mem_fop->disk_file_op;
+ disk_file_op->delta_us = get_delta_ts(s, &start_time);
+ get_syscall(s, syscall);
+ disk_file_op->file_op = map_syscall(syscall);
+ get_pathname(s, path, disk_file_op->file_op);
+ db_node = files_db_add(path);
+ disk_file_op->fileno = files_db_get_fileno(db_node);
+ switch (disk_file_op->file_op) {
+ case IOSHARK_LLSEEK:
+ case IOSHARK_LSEEK:
+ get_lseek_offset_action(s,
+ disk_file_op->file_op,
+ &disk_file_op->lseek_offset,
+ lseek_action_str);
+ disk_file_op->lseek_action =
+ map_lseek_action(lseek_action_str);
+ if (disk_file_op->lseek_action == SEEK_SET)
+ files_db_update_size(db_node,
+ disk_file_op->lseek_offset);
+ break;
+ case IOSHARK_PREAD64:
+ case IOSHARK_PWRITE64:
+ get_prw64_offset_len(s,
+ &disk_file_op->prw_offset,
+ (u_int64_t *)&disk_file_op->prw_len);
+ files_db_update_size(db_node,
+ disk_file_op->prw_offset +
+ disk_file_op->prw_len);
+ break;
+ case IOSHARK_READ:
+ case IOSHARK_WRITE:
+ get_rw_len(s, (u_int64_t *)&disk_file_op->rw_len);
+ files_db_add_to_size(db_node,
+ disk_file_op->rw_len);
+ break;
+ case IOSHARK_MMAP:
+ case IOSHARK_MMAP2:
+ get_mmap_offset_len_prot(s,
+ &disk_file_op->mmap_prot,
+ &disk_file_op->mmap_offset,
+ (u_int64_t *)&disk_file_op->mmap_len);
+ files_db_update_size(db_node,
+ disk_file_op->mmap_offset +
+ disk_file_op->mmap_len);
+ break;
+ case IOSHARK_OPEN:
+ disk_file_op->open_mode = 0;
+ get_openat_flags_mode(s, open_flags_str,
+ &disk_file_op->open_mode);
+ disk_file_op->open_flags =
+ map_open_flags(open_flags_str);
+ break;
+ case IOSHARK_FSYNC:
+ case IOSHARK_FDATASYNC:
+ break;
+ case IOSHARK_CLOSE:
+ break;
+ default:
+ break;
+ }
+ /* Chain at the end */
+ num_io_operations++;
+ in_mem_fop->next = NULL;
+ if (in_mem_file_op_head == NULL) {
+ in_mem_file_op_tail = in_mem_fop;
+ in_mem_file_op_head = in_mem_fop;
+ } else {
+ in_mem_file_op_tail->next = in_mem_fop;
+ in_mem_file_op_tail = in_mem_fop;
+ }
+ }
+ fclose(fp);
+ /*
+ * Now we can write everything out to the output tracefile.
+ */
+ fp = fopen(outfile, "w+");
+ if (fp == NULL) {
+ fprintf(stderr, "%s Can't open trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ header.num_io_operations = num_io_operations;
+ header.num_files = files_db_get_total_obj();
+ if (fwrite(&header, sizeof(struct ioshark_header), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ files_db_write_objects(fp);
+ while (in_mem_file_op_head != NULL) {
+ struct in_mem_file_op *temp;
+
+ disk_file_op = &in_mem_file_op_head->disk_file_op;
+ if (fwrite(disk_file_op,
+ sizeof(struct ioshark_file_operation), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ temp = in_mem_file_op_head;
+ in_mem_file_op_head = in_mem_file_op_head->next;
+ free(temp);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define FILE_DB_HASHSIZE 8192
+
+struct files_db_s {
+ char *filename;
+ int fileno;
+ struct files_db_s *next;
+ size_t size;
+};
+
+/* Lifted from Wikipedia Jenkins Hash function page */
+static inline u_int32_t
+jenkins_one_at_a_time_hash(char *key, size_t len)
+{
+ u_int32_t hash, i;
+
+ for(hash = i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+static inline void
+files_db_update_size(void *node, u_int64_t new_size)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->size < new_size)
+ db_node->size = new_size;
+}
+
+static inline void
+files_db_add_to_size(void *node, u_int64_t size_incr)
+{
+ ((struct files_db_s *)node)->size += size_incr;
+}
+
+static inline int
+files_db_get_fileno(void *node)
+{
+ return (((struct files_db_s *)node)->fileno);
+}
+
+static inline char *
+files_db_get_filename(void *node)
+{
+ return (((struct files_db_s *)node)->filename);
+}
+
+
+void *files_db_create_handle(void);
+void files_db_write_objects(FILE *fp);
+void *files_db_add(char *filename);
+void *files_db_lookup(char *filename);
+int files_db_get_total_obj(void);
+
+
+
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include "ioshark.h"
+#include "compile_ioshark.h"
+
+extern char *progname;
+
+static struct files_db_s *files_db_buckets[FILE_DB_HASHSIZE];
+static int current_fileno = 1;
+static int num_objects = 0;
+
+void
+files_db_write_objects(FILE *fp)
+{
+ int i;
+ struct ioshark_file_state st;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ struct files_db_s *db_node, *s;
+
+ db_node = files_db_buckets[i];
+ while (db_node != NULL) {
+ st.fileno = db_node->fileno;
+ st.size = db_node->size;
+ if (fwrite(&st, sizeof(st), 1, fp) != 1) {
+ fprintf(stderr, "%s Write error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ s = db_node;
+ db_node = db_node->next;
+ free(s->filename);
+ free(s);
+ }
+ }
+}
+
+void *files_db_lookup(char *pathname)
+{
+ u_int32_t hash;
+ struct files_db_s *db_node;
+
+ hash = jenkins_one_at_a_time_hash(pathname, strlen(pathname));
+ hash %= FILE_DB_HASHSIZE;
+ db_node = files_db_buckets[hash];
+ while (db_node != NULL) {
+ if (strcmp(db_node->filename, pathname) == 0)
+ break;
+ db_node = db_node->next;
+ }
+ return db_node;
+}
+
+void *files_db_add(char *filename)
+{
+ u_int32_t hash;
+ struct files_db_s *db_node;
+
+ if ((db_node = files_db_lookup(filename)))
+ return db_node;
+ hash = jenkins_one_at_a_time_hash(filename, strlen(filename));
+ hash %= FILE_DB_HASHSIZE;
+ db_node = malloc(sizeof(struct files_db_s));
+ db_node->filename = strdup(filename);
+ db_node->fileno = current_fileno++;
+ db_node->next = files_db_buckets[hash];
+ db_node->size = 0;
+ files_db_buckets[hash] = db_node;
+ num_objects++;
+ return db_node;
+}
+
+int
+files_db_get_total_obj(void)
+{
+ return num_objects;
+}
+
+
+
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Format of the parsed workload files.
+ * 1) Header
+ * 2) Table of the entries, each entry describes 1 file
+ * 3) Table of IO operations to perform on the files
+ */
+
+/*
+ * The parsed workload file starts off with the header, which
+ * contains the count of the total # of files that are operated on.
+ * and the total number of IO operations.
+ */
+struct ioshark_header {
+ int num_files;
+ int num_io_operations;
+};
+
+/*
+ * After the header, we have a table of #files entries. Each entry
+ * in this table describes 1 file, indexed by fileno and with the
+ * specified size.
+ * Before the tests starts, these files are pre-created.
+ */
+struct ioshark_file_state {
+ int fileno; /* 1..num_files, with files name ioshark.<fileno> */
+ size_t size;
+};
+
+enum file_op {
+ IOSHARK_LSEEK = 0,
+ IOSHARK_LLSEEK,
+ IOSHARK_PREAD64,
+ IOSHARK_PWRITE64,
+ IOSHARK_READ,
+ IOSHARK_WRITE,
+ IOSHARK_MMAP,
+ IOSHARK_MMAP2,
+ IOSHARK_OPEN,
+ IOSHARK_FSYNC,
+ IOSHARK_FDATASYNC,
+ IOSHARK_CLOSE,
+ IOSHARK_MAPPED_PREAD,
+ IOSHARK_MAPPED_PWRITE,
+ IOSHARK_MAX_FILE_OP
+};
+
+/* mmap prot flags */
+#define IOSHARK_PROT_READ 0x1
+#define IOSHARK_PROT_WRITE 0x2
+
+/*
+ * Next we have the table of IO operatiosn to perform. Each
+ * IO operation is described by this entry.
+ */
+struct ioshark_file_operation {
+ /* delta us between previous file op and this */
+ u_int64_t delta_us;
+ enum file_op file_op;
+ int fileno;
+ union {
+ struct lseek_args {
+#define lseek_offset u.lseek_a.offset
+#define lseek_action u.lseek_a.action
+ off_t offset;
+ int action;
+ } lseek_a;
+ struct prw_args {
+#define prw_offset u.prw_a.offset
+#define prw_len u.prw_a.len
+ off_t offset;
+ size_t len;
+ } prw_a;
+#define rw_len u.rw_a.len
+ struct rw_args {
+ size_t len;
+ } rw_a;
+#define mmap_offset u.mmap_a.offset
+#define mmap_len u.mmap_a.len
+#define mmap_prot u.mmap_a.prot
+ struct mmap_args {
+ off_t offset;
+ size_t len;
+ int prot;
+ } mmap_a;
+#define open_flags u.open_a.flags
+#define open_mode u.open_a.mode
+ struct open_args {
+ int flags;
+ mode_t mode;
+ } open_a;
+ } u;
+};
--- /dev/null
+/*
+ * 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 "ioshark.h"
+#define IOSHARK_MAIN
+#include "ioshark_bench.h"
+
+char *progname;
+
+#define MAX_INPUT_FILES 1024
+#define MAX_THREADS 1024
+
+struct thread_state_s {
+ char *filename;
+ FILE *fp;
+ int num_files;
+ void *db_handle;
+};
+
+struct thread_state_s thread_state[MAX_INPUT_FILES];
+int num_input_files = 0;
+int next_input_file;
+
+pthread_t tid[MAX_THREADS];
+
+/*
+ * Global options
+ */
+int do_delay = 0;
+int verbose = 0;
+
+#if 0
+static long gettid()
+{
+ return syscall(__NR_gettid);
+}
+#endif
+
+void usage()
+{
+ fprintf(stderr, "%s [-d preserve_delays] [-n num_iterations] [-t num_threads] <list of parsed input files>\n",
+ progname);
+ exit(EXIT_FAILURE);
+}
+
+pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER;
+struct timeval aggregate_file_create_time;
+struct timeval debug_file_create_time;
+struct timeval aggregate_file_remove_time;
+struct timeval aggregate_IO_time;
+struct timeval aggregate_delay_time;
+
+u_int64_t aggr_op_counts[IOSHARK_MAX_FILE_OP];
+struct rw_bytes_s aggr_io_rw_bytes;
+struct rw_bytes_s aggr_create_rw_bytes;
+
+/*
+ * Locking needed here because aggregate_delay_time is updated
+ * from multiple threads concurrently.
+ */
+static void
+update_time(struct timeval *aggr_time,
+ struct timeval *delta_time)
+{
+ struct timeval tmp;
+
+ pthread_mutex_lock(&time_mutex);
+ timeradd(aggr_time, delta_time, &tmp);
+ *aggr_time = tmp;
+ pthread_mutex_unlock(&time_mutex);
+}
+
+static void
+update_op_counts(u_int64_t *op_counts)
+{
+ int i;
+
+ pthread_mutex_lock(&stats_mutex);
+ for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++)
+ aggr_op_counts[i] += op_counts[i];
+ pthread_mutex_unlock(&stats_mutex);
+}
+
+static void
+update_byte_counts(struct rw_bytes_s *dest, struct rw_bytes_s *delta)
+{
+ pthread_mutex_lock(&stats_mutex);
+ dest->bytes_read += delta->bytes_read;
+ dest->bytes_written += delta->bytes_written;
+ pthread_mutex_unlock(&stats_mutex);
+}
+
+static int work_next_file;
+static int work_num_files;
+
+void
+init_work(int next_file, int num_files)
+{
+ pthread_mutex_lock(&work_mutex);
+ work_next_file = next_file;
+ work_num_files = work_next_file + num_files;
+ pthread_mutex_unlock(&work_mutex);
+}
+
+/* Dole out the next file to work on to the thread */
+static struct thread_state_s *
+get_work()
+{
+ struct thread_state_s *work = NULL;
+
+ pthread_mutex_lock(&work_mutex);
+ if (work_next_file < work_num_files)
+ work = &thread_state[work_next_file++];
+ pthread_mutex_unlock(&work_mutex);
+ return work;
+}
+
+static void
+create_files(struct thread_state_s *state)
+{
+ int i;
+ struct ioshark_file_state file_state;
+ char path[512];
+ void *db_node;
+ struct rw_bytes_s rw_bytes;
+
+ memset(&rw_bytes, 0, sizeof(struct rw_bytes_s));
+ for (i = 0 ; i < state->num_files ; i++) {
+ if (fread(&file_state, sizeof(struct ioshark_file_state),
+ 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error tracefile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ sprintf(path, "file.%d.%d",
+ (int)(state - thread_state),
+ file_state.fileno);
+ create_file(path, file_state.size,
+ &rw_bytes);
+ db_node = files_db_add_byfileno(state->db_handle,
+ file_state.fileno);
+ files_db_update_size(db_node, file_state.size);
+ files_db_update_filename(db_node, path);
+ }
+ update_byte_counts(&aggr_create_rw_bytes, &rw_bytes);
+}
+
+static void
+do_one_io(void *db_node,
+ struct ioshark_file_operation *file_op,
+ u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes,
+ char **bufp, int *buflen)
+{
+ assert(file_op->file_op < IOSHARK_MAX_FILE_OP);
+ op_counts[file_op->file_op]++;
+ switch (file_op->file_op) {
+ int ret;
+ char *p;
+ int fd;
+
+ case IOSHARK_LSEEK:
+ case IOSHARK_LLSEEK:
+ ret = lseek(files_db_get_fd(db_node),
+ file_op->lseek_offset,
+ file_op->lseek_action);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: lseek(%s %lu %d) returned error %d\n",
+ progname, files_db_get_filename(db_node),
+ file_op->lseek_offset,
+ file_op->lseek_action, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_PREAD64:
+ p = get_buf(bufp, buflen, file_op->prw_len, 0);
+ ret = pread(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ rw_bytes->bytes_read += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pread(%s %zu %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->prw_len,
+ file_op->prw_offset, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_PWRITE64:
+ p = get_buf(bufp, buflen, file_op->prw_len, 1);
+ ret = pwrite(files_db_get_fd(db_node), p,
+ file_op->prw_len, file_op->prw_offset);
+ rw_bytes->bytes_written += file_op->prw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: pwrite(%s %zu %lu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->prw_len,
+ file_op->prw_offset, errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_READ:
+ p = get_buf(bufp, buflen, file_op->rw_len, 0);
+ ret = read(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ rw_bytes->bytes_read += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: read(%s %zu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->rw_len,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_WRITE:
+ p = get_buf(bufp, buflen, file_op->rw_len, 1);
+ ret = write(files_db_get_fd(db_node), p,
+ file_op->rw_len);
+ rw_bytes->bytes_written += file_op->rw_len;
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: write(%s %zu) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->rw_len,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case IOSHARK_MMAP:
+ case IOSHARK_MMAP2:
+ ioshark_handle_mmap(db_node, file_op,
+ bufp, buflen, op_counts,
+ rw_bytes);
+ break;
+ case IOSHARK_OPEN:
+ if (file_op->open_flags & O_CREAT) {
+ fd = open(files_db_get_filename(db_node),
+ file_op->open_flags,
+ file_op->open_mode);
+ if (fd < 0) {
+ /*
+ * EEXIST error acceptable, others are fatal.
+ * Although we failed to O_CREAT the file (O_EXCL)
+ * We will force an open of the file before any
+ * IO.
+ */
+ if (errno == EEXIST) {
+ return;
+ } else {
+ fprintf(stderr,
+ "%s: O_CREAT open(%s %x %o) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->open_flags,
+ file_op->open_mode, errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else {
+ fd = open(files_db_get_filename(db_node),
+ file_op->open_flags);
+ if (fd < 0) {
+ if (file_op->open_flags & O_DIRECTORY) {
+ /* O_DIRECTORY open()s should fail */
+ return;
+ } else {
+ fprintf(stderr,
+ "%s: open(%s %x) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ file_op->open_flags,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ files_db_close_fd(db_node);
+ files_db_update_fd(db_node, fd);
+ break;
+ case IOSHARK_FSYNC:
+ case IOSHARK_FDATASYNC:
+ if (file_op->file_op == IOSHARK_FSYNC) {
+ ret = fsync(files_db_get_fd(db_node));
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: fsync(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ ret = fdatasync(files_db_get_fd(db_node));
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: fdatasync(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ case IOSHARK_CLOSE:
+ ret = close(files_db_get_fd(db_node));
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: close(%s) error %d\n",
+ progname,
+ files_db_get_filename(db_node), errno);
+ exit(EXIT_FAILURE);
+ }
+ files_db_update_fd(db_node, -1);
+ break;
+ default:
+ fprintf(stderr, "%s: unknown FILE_OP %d\n",
+ progname, file_op->file_op);
+ exit(EXIT_FAILURE);
+ break;
+ }
+}
+
+static void
+do_io(struct thread_state_s *state)
+{
+ void *db_node;
+ struct ioshark_header header;
+ struct ioshark_file_operation file_op;
+ u_int64_t prev_delay = 0;
+ int fd;
+ int i;
+ char *buf = NULL;
+ int buflen = 0;
+ struct timeval total_delay_time;
+ u_int64_t op_counts[IOSHARK_MAX_FILE_OP];
+ struct rw_bytes_s rw_bytes;
+
+ rewind(state->fp);
+ if (fread(&header, sizeof(struct ioshark_header), 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, state->filename);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * First open and pre-create all the files. Indexed by fileno.
+ */
+ timerclear(&total_delay_time);
+ memset(&rw_bytes, 0, sizeof(struct rw_bytes_s));
+ memset(op_counts, 0, sizeof(op_counts));
+ fseek(state->fp,
+ sizeof(struct ioshark_header) +
+ header.num_files * sizeof(struct ioshark_file_state),
+ SEEK_SET);
+ /*
+ * Loop over all the IOs, and launch each
+ */
+ for (i = 0 ; i < header.num_io_operations ; i++) {
+ if (fread(&file_op, sizeof(struct ioshark_file_operation),
+ 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error trace.outfile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ if (do_delay) {
+ struct timeval start;
+
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ usleep(file_op.delta_us - prev_delay);
+ update_delta_time(&start, &total_delay_time);
+ prev_delay = file_op.delta_us;
+ }
+ db_node = files_db_lookup_byfileno(state->db_handle,
+ file_op.fileno);
+ if (db_node == NULL) {
+ fprintf(stderr,
+ "%s Can't lookup fileno %d, fatal error\n",
+ progname, file_op.fileno);
+ exit(EXIT_FAILURE);
+ }
+ if (file_op.file_op != IOSHARK_OPEN &&
+ files_db_get_fd(db_node) == -1) {
+ /*
+ * This is a hack to workaround the fact that we did not
+ * see an open() for this file until now. open() the file
+ * O_RDWR, so that we can perform the IO.
+ */
+ fd = open(files_db_get_filename(db_node), O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "%s: open(%s O_RDWR) error %d\n",
+ progname, files_db_get_filename(db_node),
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ files_db_update_fd(db_node, fd);
+ }
+ do_one_io(db_node, &file_op,
+ op_counts, &rw_bytes, &buf, &buflen);
+ }
+ files_db_fsync_discard_files(state->db_handle);
+ files_db_close_files(state->db_handle);
+ update_time(&aggregate_delay_time, &total_delay_time);
+ update_op_counts(op_counts);
+ update_byte_counts(&aggr_io_rw_bytes, &rw_bytes);
+}
+
+void *
+io_thread(void *unused __attribute__((unused)))
+{
+ struct thread_state_s *state;
+
+ srand(gettid());
+ while ((state = get_work()))
+ do_io(state);
+ pthread_exit(NULL);
+ return(NULL);
+}
+
+static void
+do_create(struct thread_state_s *state)
+{
+ struct ioshark_header header;
+
+ if (fread(&header, sizeof(struct ioshark_header), 1, state->fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, state->filename);
+ exit(EXIT_FAILURE);
+ }
+ state->num_files = header.num_files;
+ state->db_handle = files_db_create_handle();
+ create_files(state);
+}
+
+void *
+create_files_thread(void *unused __attribute__((unused)))
+{
+ struct thread_state_s *state;
+
+ while ((state = get_work()))
+ do_create(state);
+ pthread_exit(NULL);
+ return(NULL);
+}
+
+int
+get_start_end(int *start_ix)
+{
+ int i, j, ret_numfiles;
+ u_int64_t free_fs_bytes;
+ char *infile;
+ FILE *fp;
+ struct ioshark_header header;
+ struct ioshark_file_state file_state;
+ struct statfs fsstat;
+ static int fssize_clamp_next_index = 0;
+ static int chunk = 0;
+
+ if (fssize_clamp_next_index == num_input_files)
+ return 0;
+ if (statfs("/data/local/tmp", &fsstat) < 0) {
+ fprintf(stderr, "%s: Can't statfs /data/local/tmp\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ free_fs_bytes = (fsstat.f_bavail * fsstat.f_bsize) * 9 /10;
+ for (i = fssize_clamp_next_index; i < num_input_files; i++) {
+ infile = thread_state[i].filename;
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't open %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ if (fread(&header, sizeof(struct ioshark_header),
+ 1, fp) != 1) {
+ fprintf(stderr, "%s read error %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ }
+ for (j = 0 ; j < header.num_files ; j++) {
+ if (fread(&file_state, sizeof(struct ioshark_file_state),
+ 1, fp) != 1) {
+ fprintf(stderr, "%s read error tracefile\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ if (file_state.size > free_fs_bytes) {
+ fclose(fp);
+ goto out;
+ }
+ free_fs_bytes -= file_state.size;
+ }
+ fclose(fp);
+ }
+out:
+ if (verbose) {
+ if (chunk > 0 || i < num_input_files) {
+ printf("Breaking up input files, Chunk %d: %d to %d\n",
+ chunk++, fssize_clamp_next_index, i - 1);
+ } else {
+ printf("Entire Dataset fits start = %d to %d, free_bytes = %ju\n",
+ fssize_clamp_next_index,
+ i - fssize_clamp_next_index,
+ free_fs_bytes);
+ }
+ }
+ *start_ix = fssize_clamp_next_index;
+ ret_numfiles = i - fssize_clamp_next_index;
+ fssize_clamp_next_index = i;
+ return ret_numfiles;
+}
+
+int
+ioshark_pthread_create(pthread_t *tidp, void *(*start_routine)(void *))
+{
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setstacksize(&attr, (size_t)(1024*1024));
+ return pthread_create(tidp, &attr, start_routine, (void *)NULL);
+}
+
+void
+wait_for_threads(int num_threads)
+{
+ int i;
+
+ for (i = 0; i < num_threads; i++) {
+ pthread_join(tid[i], NULL);
+ tid[i] = 0;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ FILE *fp;
+ struct stat st;
+ char *infile;
+ int num_threads = 0;
+ int num_iterations = 1;
+ int c;
+ int num_files, start_file;
+ struct thread_state_s *state;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "dn:t:v")) != EOF) {
+ switch (c) {
+ case 'd':
+ do_delay = 1;
+ break;
+ case 'n':
+ num_iterations = atoi(optarg);
+ break;
+ case 't':
+ num_threads = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (num_threads > MAX_THREADS)
+ usage();
+
+ if (optind == argc)
+ usage();
+
+ for (i = optind; i < argc; i++) {
+ infile = argv[i];
+ if (stat(infile, &st) < 0) {
+ fprintf(stderr, "%s: Can't stat %s\n",
+ progname, infile);
+ exit(EXIT_FAILURE);
+ continue;
+ }
+ if (st.st_size == 0) {
+ fprintf(stderr, "%s: Empty file %s\n",
+ progname, infile);
+ continue;
+ }
+ fp = fopen(infile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't open %s\n",
+ progname, infile);
+ continue;
+ }
+ thread_state[num_input_files].filename = infile;
+ thread_state[num_input_files].fp = fp;
+ num_input_files++;
+ }
+
+ if (num_input_files == 0) {
+ exit(EXIT_SUCCESS);
+ }
+ if (verbose) {
+ printf("Total Input Files = %d\n", num_input_files);
+ printf("Num Iterations = %d\n", num_iterations);
+ }
+ timerclear(&aggregate_file_create_time);
+ timerclear(&aggregate_file_remove_time);
+ timerclear(&aggregate_IO_time);
+
+ capture_util_state_before();
+
+ /*
+ * We pre-create the files that we need once and then we
+ * loop around N times doing IOs on the pre-created files.
+ *
+ * get_start_end() breaks up the total work here to make sure
+ * that all the files we need to pre-create fit into the
+ * available space in /data/local/tmp (hardcoded for now).
+ *
+ * If it won't fit, then we do several sweeps.
+ */
+ while ((num_files = get_start_end(&start_file))) {
+ struct timeval time_for_pass;
+
+ /* Create files once */
+ printf("Doing Pre-creation of Files\n");
+ if (num_threads == 0 || num_threads > num_files)
+ num_threads = num_files;
+ (void)system("echo 3 > /proc/sys/vm/drop_caches");
+ init_work(start_file, num_files);
+ (void)gettimeofday(&time_for_pass,
+ (struct timezone *)NULL);
+ for (i = 0; i < num_threads; i++) {
+ if (ioshark_pthread_create(&(tid[i]),
+ create_files_thread)) {
+ fprintf(stderr,
+ "%s: Can't create creator thread %d\n",
+ progname, i);
+ exit(EXIT_FAILURE);
+ }
+ }
+ wait_for_threads(num_threads);
+ update_delta_time(&time_for_pass, &aggregate_file_create_time);
+
+ /* Do the IOs N times */
+ for (i = 0 ; i < num_iterations ; i++) {
+ (void)system("echo 3 > /proc/sys/vm/drop_caches");
+ if (num_iterations > 1)
+ printf("Starting Test. Iteration %d...\n",
+ i);
+ else
+ printf("Starting Test...\n");
+ init_work(start_file, num_files);
+ (void)gettimeofday(&time_for_pass,
+ (struct timezone *)NULL);
+ for (c = 0; c < num_threads; c++) {
+ if (ioshark_pthread_create(&(tid[c]),
+ io_thread)) {
+ fprintf(stderr,
+ "%s: Can't create thread %d\n",
+ progname, c);
+ exit(EXIT_FAILURE);
+ }
+ }
+ wait_for_threads(num_threads);
+ update_delta_time(&time_for_pass,
+ &aggregate_IO_time);
+ }
+
+ /*
+ * We are done with the N iterations of IO.
+ * Destroy the files we pre-created.
+ */
+ init_work(start_file, num_files);
+ while ((state = get_work())) {
+ struct timeval start;
+
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+ files_db_unlink_files(state->db_handle);
+ update_delta_time(&start, &aggregate_file_remove_time);
+ files_db_free_memory(state->db_handle);
+ }
+ }
+ printf("Total Creation time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_file_create_time),
+ get_usecs(&aggregate_file_create_time));
+ printf("Total Remove time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_file_remove_time),
+ get_usecs(&aggregate_file_remove_time));
+ if (do_delay)
+ printf("Total delay time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_delay_time),
+ get_usecs(&aggregate_delay_time));
+ printf("Total Test (IO) time = %ju.%ju (msecs.usecs)\n",
+ get_msecs(&aggregate_IO_time),
+ get_usecs(&aggregate_IO_time));
+ if (verbose)
+ print_bytes("Upfront File Creation bytes",
+ &aggr_create_rw_bytes);
+ print_bytes("Total Test (IO) bytes", &aggr_io_rw_bytes);
+ if (verbose)
+ print_op_stats(aggr_op_counts);
+ report_cpu_disk_util();
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef IOSHARK_MAIN
+const char *IO_op[] = {
+ "LSEEK",
+ "LLSEEK",
+ "PREAD64",
+ "PWRITE64",
+ "READ",
+ "WRITE",
+ "MMAP",
+ "MMAP2",
+ "OPEN",
+ "FSYNC",
+ "FDATASYNC",
+ "CLOSE",
+ "MAPPED_PREAD",
+ "MAPPED_PWRITE",
+ "MAX_FILE_OP"
+};
+#endif
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+
+#define MINBUFLEN (16*1024)
+
+#define FILE_DB_HASHSIZE 8192
+
+struct files_db_s {
+ char *filename;
+ int fileno;
+ size_t size;
+ int fd;
+ int debug_open_flags;
+ struct files_db_s *next;
+};
+
+struct files_db_handle {
+ struct files_db_s *files_db_buckets[FILE_DB_HASHSIZE];
+};
+
+struct IO_operation_s {
+ char *IO_op;
+};
+
+struct rw_bytes_s {
+ u_int64_t bytes_read;
+ u_int64_t bytes_written;
+};
+
+static inline void
+files_db_update_size(void *node, u_int64_t new_size)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->size < new_size)
+ db_node->size = new_size;
+}
+
+static inline void
+files_db_update_filename(void *node, char *filename)
+{
+ ((struct files_db_s *)node)->filename = strdup(filename);
+}
+
+static inline int
+files_db_get_fileno(void *node)
+{
+ return (((struct files_db_s *)node)->fileno);
+}
+
+static inline int
+files_db_get_fd(void *node)
+{
+ return (((struct files_db_s *)node)->fd);
+}
+
+static inline char *
+files_db_get_filename(void *node)
+{
+ return (((struct files_db_s *)node)->filename);
+}
+
+static inline u_int64_t
+get_msecs(struct timeval *tv)
+{
+ return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000));
+}
+
+static inline u_int64_t
+get_usecs(struct timeval *tv)
+{
+ return (tv->tv_usec % 1000);
+}
+
+static inline void
+update_delta_time(struct timeval *start,
+ struct timeval *destination)
+{
+ struct timeval res, finish;
+
+ (void)gettimeofday(&finish, (struct timezone *)NULL);
+ timersub(&finish, start, &res);
+ timeradd(destination, &res, &finish);
+ *destination = finish;
+}
+
+void *files_db_create_handle(void);
+void *files_db_lookup_byfileno(void *handle, int fileno);
+void *files_db_add_byfileno(void *handle, int fileno);
+void files_db_update_fd(void *node, int fd);
+void files_db_unlink_files(void *db_handle);
+void files_db_close_files(void *handle);
+void files_db_close_fd(void *node);
+void files_db_free_memory(void *handle);
+void create_file(char *path, size_t size,
+ struct rw_bytes_s *rw_bytes);
+char *get_buf(char **buf, int *buflen, int len, int do_fill);
+void files_db_fsync_discard_files(void *handle);
+void print_op_stats(u_int64_t *op_counts);
+void print_bytes(char *desc, struct rw_bytes_s *rw_bytes);
+void ioshark_handle_mmap(void *db_node,
+ struct ioshark_file_operation *file_op,
+ char **bufp, int *buflen, u_int64_t *op_counts,
+ struct rw_bytes_s *rw_bytes);
+void capture_util_state_before(void);
+void report_cpu_disk_util(void);
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include "ioshark.h"
+#include "ioshark_bench.h"
+
+extern char *progname;
+
+void *
+files_db_create_handle(void)
+{
+ struct files_db_handle *h;
+ int i;
+
+ h = malloc(sizeof(struct files_db_handle));
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++)
+ h->files_db_buckets[i] = NULL;
+ return h;
+}
+
+void *files_db_lookup_byfileno(void *handle, int fileno)
+{
+ u_int32_t hash;
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+
+ hash = fileno % FILE_DB_HASHSIZE;
+ db_node = h->files_db_buckets[hash];
+ while (db_node != NULL) {
+ if (db_node->fileno == fileno)
+ break;
+ db_node = db_node->next;
+ }
+ return db_node;
+}
+
+void *files_db_add_byfileno(void *handle, int fileno)
+{
+ u_int32_t hash = fileno % FILE_DB_HASHSIZE;
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+
+ db_node = (struct files_db_s *)
+ files_db_lookup_byfileno(handle, fileno);
+ if (db_node == NULL) {
+ db_node = malloc(sizeof(struct files_db_s));
+ db_node->fileno = fileno;
+ db_node->filename = NULL;
+ db_node->size = 0;
+ db_node->fd = -1;
+ db_node->next = h->files_db_buckets[hash];
+ h->files_db_buckets[hash] = db_node;
+ } else {
+ fprintf(stderr,
+ "%s: Node to be added already exists fileno = %d\n\n",
+ __func__, fileno);
+ exit(EXIT_FAILURE);
+ }
+ return db_node;
+}
+
+void
+files_db_fsync_discard_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ int do_close = 0;
+
+ if (db_node->fd == -1) {
+ int fd;
+
+ /*
+ * File was closed, let's open it so we can
+ * fsync and fadvise(DONTNEED) it.
+ */
+ do_close = 1;
+ fd = open(files_db_get_filename(db_node),
+ O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr,
+ "%s: open(%s O_RDWR) error %d\n",
+ progname, db_node->filename,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ db_node->fd = fd;
+ }
+ if (fsync(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot fsync %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ if (posix_fadvise(db_node->fd, 0, 0,
+ POSIX_FADV_DONTNEED) < 0) {
+ fprintf(stderr,
+ "%s: Cannot fadvise(DONTNEED) %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ if (do_close) {
+ close(db_node->fd);
+ db_node->fd = -1;
+ }
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_update_fd(void *node, int fd)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ db_node->fd = fd;
+}
+
+void
+files_db_close_fd(void *node)
+{
+ struct files_db_s *db_node = (struct files_db_s *)node;
+
+ if (db_node->fd != -1)
+ close(db_node->fd);
+ db_node->fd = -1;
+}
+
+void
+files_db_close_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ if ((db_node->fd != -1) && close(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot close %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ db_node->fd = -1;
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_unlink_files(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ if ((db_node->fd != -1) && close(db_node->fd) < 0) {
+ fprintf(stderr, "%s: Cannot close %s\n",
+ __func__, db_node->filename);
+ exit(1);
+ }
+ db_node->fd = -1;
+ if (unlink(db_node->filename) < 0) {
+ fprintf(stderr, "%s: Cannot unlink %s:%s\n",
+ __func__, db_node->filename, strerror(errno));
+ exit(1);
+ }
+ db_node = db_node->next;
+ }
+ }
+}
+
+void
+files_db_free_memory(void *handle)
+{
+ struct files_db_handle *h = (struct files_db_handle *)handle;
+ struct files_db_s *db_node, *tmp;
+ int i;
+
+ for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
+ db_node = h->files_db_buckets[i];
+ while (db_node != NULL) {
+ tmp = db_node;
+ db_node = db_node->next;
+ free(tmp->filename);
+ free(tmp);
+ }
+ }
+ free(h);
+}
+
+char *
+get_buf(char **buf, int *buflen, int len, int do_fill __attribute__((unused)))
+{
+ if (len == 0 && *buf == NULL) {
+ /*
+ * If we ever get a zero len
+ * request, start with MINBUFLEN
+ */
+ if (*buf == NULL)
+ len = MINBUFLEN / 2;
+ }
+ if (*buflen < len) {
+ *buflen = MAX(MINBUFLEN, len * 2);
+ if (*buf)
+ free(*buf);
+ *buf = malloc(*buflen);
+ if (do_fill) {
+ u_int32_t *s;
+ int count;
+
+ s = (u_int32_t *)*buf;
+ count = *buflen / sizeof(u_int32_t);
+ while (count > 0) {
+ *s++ = rand();
+ count--;
+ }
+ }
+ }
+ assert(*buf != NULL);
+ return *buf;
+}
+
+void
+create_file(char *path, size_t size, struct rw_bytes_s *rw_bytes)
+{
+ int fd, n;
+ char *buf = NULL;
+ int buflen = 0;
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "%s Cannot create file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ while (size > 0) {
+ n = MIN(size, MINBUFLEN);
+ buf = get_buf(&buf, &buflen, n, 1);
+ if (write(fd, buf, n) < n) {
+ fprintf(stderr,
+ "%s Cannot write file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ rw_bytes->bytes_written += n;
+ size -= n;
+ }
+ if (fsync(fd) < 0) {
+ fprintf(stderr, "%s Cannot fsync file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) < 0) {
+ fprintf(stderr,
+ "%s Cannot fadvise(DONTNEED) file %s, error = %d\n",
+ progname, path, errno);
+ exit(EXIT_FAILURE);
+ }
+ close(fd);
+}
+
+void
+print_op_stats(u_int64_t *op_counts)
+{
+ int i;
+ extern char *IO_op[];
+
+ printf("IO Operation counts :\n");
+ for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++) {
+ printf("%s: %ju\n",
+ IO_op[i], op_counts[i]);
+ }
+}
+
+void
+print_bytes(char *desc, struct rw_bytes_s *rw_bytes)
+{
+ printf("%s: Reads = %dMB, Writes = %dMB\n",
+ desc,
+ (int)(rw_bytes->bytes_read / (1024 * 1024)),
+ (int)(rw_bytes->bytes_written / (1024 * 1024)));
+}
+
+struct cpu_disk_util_stats {
+ /* CPU util */
+ u_int64_t user_cpu_ticks;
+ u_int64_t nice_cpu_ticks;
+ u_int64_t system_cpu_ticks;
+ u_int64_t idle_cpu_ticks;
+ u_int64_t iowait_cpu_ticks;
+ u_int64_t hardirq_cpu_ticks;
+ u_int64_t softirq_cpu_ticks;
+ /* disk util */
+ unsigned long long uptime;
+ unsigned int tot_ticks;
+ unsigned long rd_ios;
+ unsigned long wr_ios;
+ unsigned long rd_sec;
+ unsigned long wr_sec;
+};
+
+static struct cpu_disk_util_stats before;
+static struct cpu_disk_util_stats after;
+
+#define BUFSIZE 8192
+
+static int hz;
+
+static void
+get_HZ(void)
+{
+ if ((hz = sysconf(_SC_CLK_TCK)) == -1)
+ exit(1);
+}
+
+#if 0
+static int num_cores;
+
+static void
+get_cores(void)
+{
+ if ((num_cores = sysconf(_SC_NPROCESSORS_ONLN)) == -1)
+ exit(1);
+}
+#endif
+
+static void
+read_disk_util_state(struct cpu_disk_util_stats *state)
+{
+ FILE *fp;
+ char line[BUFSIZE], dev_name[BUFSIZE];
+ unsigned int major, minor;
+ unsigned int ios_pgr;
+ unsigned int rq_ticks;
+ unsigned int wr_ticks;
+ unsigned long rd_ticks;
+ unsigned long rd_merges;
+ unsigned long wr_merges;
+ unsigned long up_sec, up_cent;
+
+ /* Read and parse /proc/uptime */
+ fp = fopen("/proc/uptime", "r");
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ fprintf(stderr, "%s: Cannot read /proc/uptime\n",
+ progname);
+ exit(1);
+ }
+ fclose(fp);
+ sscanf(line, "%lu.%lu", &up_sec, &up_cent);
+ state->uptime = (unsigned long long) up_sec * hz +
+ (unsigned long long) up_cent * hz / 100;
+ /* Read and parse /proc/diskstats */
+ fp = fopen("/proc/diskstats", "r");
+ while (fgets(line, sizeof(line), fp)) {
+ sscanf(line,
+ "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u",
+ &major, &minor, dev_name,
+ &state->rd_ios, &rd_merges, &state->rd_sec,
+ &rd_ticks, &state->wr_ios, &wr_merges,
+ &state->wr_sec, &wr_ticks,
+ &ios_pgr, &state->tot_ticks, &rq_ticks);
+ if (strcmp(dev_name, "sda") == 0) {
+ /*
+ * tot_ticks is "number of milliseconds spent
+ * doing I/Os". Look at Documentation/iostats.txt.
+ * Or at genhd.c:diskstats_show(), which calls
+ * jiffies_to_msecs() on this field before printing
+ * it. Convert this to hz, so we can do all our math
+ * in ticks.
+ */
+ state->tot_ticks /= 1000; /* to seconds */
+ state->tot_ticks *= hz; /* to hz */
+ fclose(fp);
+ return;
+ }
+ }
+ fprintf(stderr, "%s: Did not find device sda in /proc/diskstats\n",
+ progname);
+ exit(1);
+}
+
+static void
+read_cpu_util_state(struct cpu_disk_util_stats *state)
+{
+ FILE *fp;
+ char line[BUFSIZE], cpu[BUFSIZE];
+
+ /* Read and parse /proc/stat */
+ fp = fopen("/proc/stat", "r");
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ fprintf(stderr, "%s: Cannot read /proc/stat\n",
+ progname);
+ exit(1);
+ }
+ fclose(fp);
+ sscanf(line, "%s %ju %ju %ju %ju %ju %ju %ju",
+ cpu,
+ &state->user_cpu_ticks,
+ &state->nice_cpu_ticks,
+ &state->system_cpu_ticks,
+ &state->idle_cpu_ticks,
+ &state->iowait_cpu_ticks,
+ &state->hardirq_cpu_ticks,
+ &state->softirq_cpu_ticks);
+}
+
+void
+capture_util_state_before(void)
+{
+ get_HZ();
+ read_disk_util_state(&before);
+ read_cpu_util_state(&before);
+}
+
+extern int verbose;
+
+void
+report_cpu_disk_util(void)
+{
+ double disk_util, cpu_util;
+ u_int64_t tot1, tot2, delta1, delta2;
+
+ read_disk_util_state(&after);
+ read_cpu_util_state(&after);
+ /* CPU Util */
+ tot2 = after.user_cpu_ticks + after.nice_cpu_ticks +
+ after.system_cpu_ticks + after.hardirq_cpu_ticks +
+ after.softirq_cpu_ticks;
+ tot1 = before.user_cpu_ticks + before.nice_cpu_ticks +
+ before.system_cpu_ticks + before.hardirq_cpu_ticks +
+ before.softirq_cpu_ticks;
+ delta1 = tot2 - tot1;
+ tot2 += after.iowait_cpu_ticks + after.idle_cpu_ticks;
+ tot1 += before.iowait_cpu_ticks + before.idle_cpu_ticks;
+ delta2 = tot2 - tot1;
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("CPU util = %.2f%%\n", cpu_util);
+ /* Next compute system (incl irq/softirq) and user cpu util */
+ delta1 = (after.user_cpu_ticks + after.nice_cpu_ticks) -
+ (before.user_cpu_ticks + before.nice_cpu_ticks);
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("User CPU util = %.2f%%\n", cpu_util);
+ delta1 = (after.system_cpu_ticks + after.hardirq_cpu_ticks +
+ after.softirq_cpu_ticks) -
+ (before.system_cpu_ticks + before.hardirq_cpu_ticks +
+ before.softirq_cpu_ticks);
+ cpu_util = delta1 * 100.0 / delta2;
+ printf("System CPU util = %.2f%%\n", cpu_util);
+ /* Disk Util */
+ disk_util = (after.tot_ticks - before.tot_ticks) * 100.0 /
+ (after.uptime - before.uptime);
+ if (verbose) {
+ printf("Reads : nr_ios %lu, MB read %lu\n",
+ (after.rd_ios - before.rd_ios),
+ (after.rd_sec - before.rd_sec) / 2048);
+ printf("Writes : nr_ios %lu, MB written %lu\n",
+ (after.wr_ios - before.wr_ios),
+ (after.wr_sec - before.wr_sec) / 2048);
+ }
+ printf("Disk util = %.2f%%\n", disk_util);
+}
#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);
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]);
//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;
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;
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;
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;
}
}
+ 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;
}
#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";
}
// 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) {
}
// 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
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;
(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++,
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);
}
}
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");
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>(
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();
}
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");
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>(
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();
}
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.
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);
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.
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);
function usage() {
cat<<EOT
Usage:
-${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-d PRODUCT_OUT] [-C FS_CONFIG ] [-c FILE_CONTEXTS] [-B BLOCK_MAP_FILE] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT] [-t COMPRESS_THRESHOLD] [-a]
+${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-d PRODUCT_OUT] [-C FS_CONFIG ] [-c FILE_CONTEXTS] [-B BLOCK_MAP_FILE] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT] [-t COMPRESS_THRESHOLD] [-w WHITELIST_FILE] [-a]
EOT
}
shift; shift
fi
+WHITELIST_FILE=
+if [[ "$1" == "-w" ]]; then
+ WHITELIST_FILE=$2
+ shift; shift
+fi
DISABLE_4K_ALIGN=false
if [[ "$1" == "-a" ]]; then
if [ "$DISABLE_4K_ALIGN" = true ]; then
OPT="$OPT -disable-4k-align"
fi
+if [ -n "$WHITELIST_FILE" ]; then
+ OPT="$OPT -whitelist $WHITELIST_FILE"
+fi
MAKE_SQUASHFS_CMD="mksquashfs $SRC_DIR/ $OUTPUT_FILE -no-progress -comp $COMPRESSOR $COMPRESSOR_OPT -no-exports -noappend -no-recovery -no-fragments -no-duplicates -android-fs-config $OPT"
echo $MAKE_SQUASHFS_CMD
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
LOCAL_MODULE_TARGET_ARCH := arm
+LOCAL_CFLAGS += -Wall -Werror
+
include $(BUILD_EXECUTABLE)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
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 ; \
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
+++ /dev/null
-/*
- * 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
-
-
+++ /dev/null
-#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;
-}
--- /dev/null
+#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;
+}
" -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"
# 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:/ {
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
echo Copying $scripts to device $devdir...
copy_files
+adb shell ln -s /data/benchmarktest/hwuimacro/$HWUIMACRO $devdir/$HWUIMACRO
tests=""
# measure background power
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"