* limitations under the License.
*/
+#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
return 0;
}
-void canned_fs_config(const char* path, int dir,
+static const int kDebugCannedFsConfig = 0;
+
+void canned_fs_config(const char* path, int dir, const char* target_out_path,
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
Path key;
key.path = path+1; // canned paths lack the leading '/'
*mode = p->mode;
*capabilities = p->capabilities;
-#if 0
- // for debugging, run the built-in fs_config and compare the results.
-
- unsigned c_uid, c_gid, c_mode;
- uint64_t c_capabilities;
- fs_config(path, dir, &c_uid, &c_gid, &c_mode, &c_capabilities);
-
- if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
- if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
- if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
- if (c_capabilities != *capabilities) printf("%s capabilities %llx %llx\n", path, *capabilities, c_capabilities);
-#endif
+ if (kDebugCannedFsConfig) {
+ // for debugging, run the built-in fs_config and compare the results.
+
+ unsigned c_uid, c_gid, c_mode;
+ uint64_t c_capabilities;
+ fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
+
+ if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
+ if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
+ if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
+ if (c_capabilities != *capabilities)
+ printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
+ path,
+ *capabilities,
+ c_capabilities);
+ }
}
#include <inttypes.h>
int load_canned_fs_config(const char* fn);
-void canned_fs_config(const char* path, int dir,
+void canned_fs_config(const char* path, int dir, const char* target_out_path,
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities);
#endif
int sock = -1;
while (true) {
- sock = socket_local_client("vold",
+ sock = socket_local_client("cryptd",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (sock >= 0) {
struct pollfd poll_sock = {sock, POLLIN, 0};
- int rc = poll(&poll_sock, 1, vold_command_timeout_ms);
+ int rc = TEMP_FAILURE_RETRY(poll(&poll_sock, 1, vold_command_timeout_ms));
if (rc < 0) {
KLOG_ERROR(TAG, "Error in poll %s\n", strerror(errno));
return "";
}
+
if (!(poll_sock.revents & POLLIN)) {
KLOG_ERROR(TAG, "Timeout\n");
return "";
}
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
- rc = read(sock, buffer, sizeof(buffer));
+ rc = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
if (rc <= 0) {
if (rc == 0) {
KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n");
void ext4_parse_sb_info(struct ext4_super_block *sb);
u16 ext4_crc16(u16 crc_in, const void *buf, int size);
-typedef void (*fs_config_func_t)(const char *path, int dir, unsigned *uid, unsigned *gid,
- unsigned *mode, uint64_t *capabilities);
+typedef void (*fs_config_func_t)(const char *path, int dir, const char *target_out_path,
+ unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
struct selabel_handle;
-int make_ext4fs_internal(int fd, const char *directory,
+int make_ext4fs_internal(int fd, const char *directory, const char *_target_out_directory,
const char *mountpoint, fs_config_func_t fs_config_func, int gzip,
int sparse, int crc, int wipe, int real_uuid,
struct selabel_handle *sehnd, int verbose, time_t fixed_time,
that does not exist on disk (e.g. lost+found).
dir_path is an absolute path, with trailing slash, to the same directory
if the image were mounted at the specified mount point */
-static u32 build_directory_structure(const char *full_path, const char *dir_path,
+static u32 build_directory_structure(const char *full_path, const char *dir_path, const char *target_out_path,
u32 dir_inode, fs_config_func_t fs_config_func,
struct selabel_handle *sehnd, int verbose, time_t fixed_time)
{
unsigned int uid = 0;
unsigned int gid = 0;
int dir = S_ISDIR(stat.st_mode);
- fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities);
+ fs_config_func(dentries[i].path, dir, target_out_path, &uid, &gid, &mode, &capabilities);
dentries[i].mode = mode;
dentries[i].uid = uid;
dentries[i].gid = gid;
ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
if (ret < 0)
critical_error_errno("asprintf");
- entry_inode = build_directory_structure(subdir_full_path,
- subdir_dir_path, inode, fs_config_func, sehnd, verbose, fixed_time);
+ entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, target_out_path,
+ inode, fs_config_func, sehnd, verbose, fixed_time);
free(subdir_full_path);
free(subdir_dir_path);
} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
reset_ext4fs_info();
info.len = len;
- return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, 0, sehnd, 0, -1, NULL);
+ return make_ext4fs_internal(fd, NULL, NULL, mountpoint, NULL, 0, 1, 0, 0, 0, sehnd, 0, -1, NULL);
}
int make_ext4fs(const char *filename, long long len,
return EXIT_FAILURE;
}
- status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd, 0, -1, NULL);
+ status = make_ext4fs_internal(fd, NULL, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd, 0, -1, NULL);
close(fd);
return status;
return canonicalize_slashes(str, false);
}
-int make_ext4fs_internal(int fd, const char *_directory,
+int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory,
const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
int sparse, int crc, int wipe, int real_uuid,
struct selabel_handle *sehnd, int verbose, time_t fixed_time,
u16 root_mode;
char *mountpoint;
char *directory = NULL;
+ char *target_out_directory = NULL;
if (setjmp(setjmp_env))
return EXIT_FAILURE; /* Handle a call to longjmp() */
directory = canonicalize_rel_slashes(_directory);
}
+ if (_target_out_directory) {
+ target_out_directory = canonicalize_rel_slashes(_target_out_directory);
+ }
+
if (info.len <= 0)
info.len = get_file_size(fd);
root_inode_num = build_default_directory_structure(mountpoint, sehnd);
#else
if (directory)
- root_inode_num = build_directory_structure(directory, mountpoint, 0,
+ root_inode_num = build_directory_structure(directory, mountpoint, target_out_directory, 0,
fs_config_func, sehnd, verbose, fixed_time);
else
root_inode_num = build_default_directory_structure(mountpoint, sehnd);
fprintf(stderr, " [ -L <label> ] [ -f ] [ -a <android mountpoint> ] [ -u ]\n");
fprintf(stderr, " [ -S file_contexts ] [ -C fs_config ] [ -T timestamp ]\n");
fprintf(stderr, " [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ] [ -B <block_list_file> ]\n");
- fprintf(stderr, " <filename> [<directory>]\n");
+ fprintf(stderr, " <filename> [[<directory>] <target_out_directory>]\n");
}
int main(int argc, char **argv)
int opt;
const char *filename = NULL;
const char *directory = NULL;
+ const char *target_out_directory = NULL;
char *mountpoint = NULL;
fs_config_func_t fs_config_func = NULL;
const char *fs_config_file = NULL;
if (optind < argc)
directory = argv[optind++];
+ if (optind < argc)
+ target_out_directory = argv[optind++];
+
if (optind < argc) {
fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
usage(argv[0]);
fd = STDOUT_FILENO;
}
- exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
+ exitcode = make_ext4fs_internal(fd, directory, target_out_directory, mountpoint, fs_config_func, gzip,
sparse, crc, wipe, real_uuid, sehnd, verbose, fixed_time, block_list_file);
close(fd);
if (block_list_file)
cat<<EOT
Usage:
mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [-j <journal_size>]
- [-T TIMESTAMP] [-C FS_CONFIG] [-B BLOCK_LIST_FILE] [-L LABEL] [FILE_CONTEXTS]
+ [-T TIMESTAMP] [-C FS_CONFIG] [-D PRODUCT_OUT] [-B BLOCK_LIST_FILE] [-L LABEL] [FILE_CONTEXTS]
EOT
}
shift; shift
fi
+PRODUCT_OUT=
+if [[ "$1" == "-D" ]]; then
+ PRODUCT_OUT=$2
+ shift; shift
+fi
+
BLOCK_LIST=
if [[ "$1" == "-B" ]]; then
BLOCK_LIST=$2
OPT="$OPT -L $LABEL"
fi
-MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE -T $TIMESTAMP $OPT -l $SIZE $JOURNAL_FLAGS -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
+MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE -T $TIMESTAMP $OPT -l $SIZE $JOURNAL_FLAGS -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR $PRODUCT_OUT"
echo $MAKE_EXT4FS_CMD
$MAKE_EXT4FS_CMD
if [ $? -ne 0 ]; then
struct f2fs_checkpoint *cp1, *cp2, *cur_cp;
int cur_cp_no;
- unsigned long blk_size;// = 1<<le32_to_cpu(info->sb->log_blocksize);
+ unsigned long blk_size;
unsigned long long cp1_version = 0, cp2_version = 0;
unsigned long long cp1_start_blk_no;
unsigned long long cp2_start_blk_no;
u32 bmp_size;
- blk_size = 1U<<le32_to_cpu(sb->log_blocksize);
+ blk_size = 1U << le32_to_cpu(sb->log_blocksize);
/*
* Find valid cp by reading both packs and finding most recent one.
u64 block;
unsigned int used, found, started = 0, i;
- for (block=startblock; block<info->total_blocks; block++) {
+ block = startblock;
+ while (block < info->total_blocks) {
/* TODO: Save only relevant portions of metadata */
if (block < info->main_blkaddr) {
if (func(block, data)) {
/* get SIT entry from SIT section */
if (!found) {
- sit_block_num_cur = segnum/SIT_ENTRY_PER_BLOCK;
+ sit_block_num_cur = segnum / SIT_ENTRY_PER_BLOCK;
sit_entry = &info->sit_blocks[sit_block_num_cur].entries[segnum % SIT_ENTRY_PER_BLOCK];
}
block_offset = (block - info->main_blkaddr) % info->blocks_per_segment;
+ if (block_offset == 0 && GET_SIT_VBLOCKS(sit_entry) == 0) {
+ block += info->blocks_per_segment;
+ continue;
+ }
+
used = f2fs_test_bit(block_offset, (char *)sit_entry->valid_map);
if(used)
if (func(block, data))
return -1;
}
+
+ block++;
}
return 0;
}
{
struct privdata *d = data;
char *buf;
- int pdone = (pos*100)/d->info->total_blocks;
+ int pdone = (pos * 100) / d->info->total_blocks;
if (pdone > d->done) {
d->done = pdone;
printf("Done with %d percent\n", d->done);
}
off64_t ret;
- ret = lseek64(d->outfd, pos*F2FS_BLKSIZE, SEEK_SET);
+ ret = lseek64(d->outfd, pos * F2FS_BLKSIZE, SEEK_SET);
if (ret < 0) {
SLOGE("failed to seek\n");
return ret;
TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
close(exec_child_fd);
errno = saved_errno;
- PLOG(FATAL) << "execvp(" << argv[0] << ") failed";
+ PLOG(ERROR) << "execvp(" << argv[0] << ") failed";
} else {
- PLOG(FATAL) << "child process failed to receive start_signal, nread = " << nread;
+ PLOG(DEBUG) << "child process failed to receive start_signal, nread = " << nread;
}
+ exit(1);
}
bool Workload::Start() {
function usage() {
cat<<EOT
Usage:
-${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-c FILE_CONTEXTS] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT]
+${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-d PRODUCT_OUT] [-c FILE_CONTEXTS] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT]
EOT
}
shift; shift
fi
+PRODUCT_OUT=
+if [[ "$1" == "-d" ]]; then
+ PRODUCT_OUT=$2
+ shift; shift
+fi
+
FILE_CONTEXTS=
if [[ "$1" == "-c" ]]; then
FILE_CONTEXTS=$2
if [ -n "$MOUNT_POINT" ]; then
OPT="$OPT -mount-point $MOUNT_POINT"
fi
+if [ -n "$PRODUCT_OUT" ]; then
+ OPT="$OPT -product-out $PRODUCT_OUT"
+fi
if [ -n "$FILE_CONTEXTS" ]; then
OPT="$OPT -context-file $FILE_CONTEXTS"
fi
# Copyright 2012 The Android Open Source Project
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= rand_emmc_perf.c
LOCAL_MODULE:= rand_emmc_perf
+LOCAL_MODULE_STEM_32:= rand_emmc_perf
+LOCAL_MODULE_STEM_64:= rand_emmc_perf64
LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(local_target_dir)
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_STATIC_LIBRARIES := libm libc
--- /dev/null
+#
+# Uncompress a file generated via atrace -z
+#
+# Usage: python atrace-uncompress.py infile > outfile
+#
+import sys, zlib
+
+def main():
+
+ if len(sys.argv) != 2:
+ print >> sys.stderr, ('Usage: %s inputfile' % sys.argv[0])
+ sys.exit(1)
+
+ infile = open(sys.argv[1], "rb")
+ out = infile.read()
+ parts = out.split('\nTRACE:', 1)
+
+ data = ''.join(parts[1])
+
+ # Remove CR characters
+ if data.startswith('\r\n'):
+ data = data.replace('\r\n', '\n')
+
+ # Skip the initial newline.
+ data = data[1:]
+
+ if not data:
+ print >> sys.stderr, ('No trace data found')
+ sys.exit(1)
+
+ out = zlib.decompress(data)
+ print(out)
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+# Capture and display input events and coordinates
+#
+# Usage: ./capture.sh
+#
+
+# do a throw-away adb in case the server is out-of-date
+adb devices -l 2>&1 >/dev/null
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ (-d) DEVICE=$2; shift;;
+ (*)
+ echo Unknown option $1
+ exit 1;;
+ esac
+ shift
+done
+
+if [ "$DEVICE" = "" ]; then
+ devInfo=$(adb devices -l | grep -v ^List | head -1)
+ set -- $devInfo
+ echo devInfo=$devInfo
+ DEVICE=$(echo $4 | sed 's/product://')
+fi
+
+function convert {
+ in=$1
+ max=$2
+ scale=$3
+ if [ $max -eq 0 ]; then
+ echo $in
+ else
+ ((out=in*scale/max))
+ echo $out
+ fi
+}
+
+
+case $DEVICE in
+(shamu|hammerhead|bullhead)
+ # no scaling necessary
+ xmax=0
+ ymax=0;;
+(volantis)
+ xmax=3060
+ xscale=1500
+ ymax=2304
+ yscale=1950;;
+(*)
+ echo "Error: No display information available for $DEVICE"
+ exit 1;;
+esac
+
+echo Capturing input for $DEVICE...
+stdbuf -o0 adb shell getevent -t |
+ stdbuf -o0 grep "event.: 0003" |
+ stdbuf -o0 grep "0003 003[0156a9]" |
+ stdbuf -o0 tr ':[]\r' ' ' | while read line
+do
+ set -- $line
+ code=$4
+ value=$((16#$5))
+ case $code in
+ (0035) x=$(convert $value $xmax $xscale);;
+ (0036) y=$(convert $value $ymax $yscale);;
+ (0030) tag="majorTouch";;
+ (0031) tag="minorTouch";;
+ (003a) tag="pressure";;
+ (0039) tag="trackingId";;
+ (--) echo unknown code=$code;;
+ esac
+ printf "%-10s %-4d %-4d\n" $tag $x $y
+done
--- /dev/null
+# functions and definitions for workload automation scripts
+#
+# See recentfling.sh, systemapps.sh, and other scripts that use
+# these definitions.
+#
+
+dflttracecategories="gfx input view am rs power sched freq idle load memreclaim"
+dfltAppList="gmail hangouts chrome youtube camera photos play maps calendar earth calculator sheets docs home"
+generateActivities=0
+
+# default activities. Can dynamically generate with -g.
+gmailActivity='com.google.android.gm/com.google.android.gm.ConversationListActivityGmail'
+hangoutsActivity='com.google.android.talk/com.google.android.talk.SigningInActivity'
+chromeActivity='com.android.chrome/_not_used'
+youtubeActivity='com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity'
+cameraActivity='com.google.android.GoogleCamera/com.android.camera.CameraActivity'
+playActivity='com.android.vending/com.google.android.finsky.activities.MainActivity'
+feedlyActivity='com.devhd.feedly/com.devhd.feedly.Main'
+photosActivity='com.google.android.apps.plus/com.google.android.apps.photos.phone.PhotosHomeActivity'
+mapsActivity='com.google.android.apps.maps/com.google.android.maps.MapsActivity'
+calendarActivity='com.google.android.calendar/com.android.calendar.AllInOneActivity'
+earthActivity='com.google.earth/com.google.earth.EarthActivity'
+calculatorActivity='com.android.calculator2/com.android.calculator2.Calculator'
+sheetsActivity='com.google.android.apps.docs.editors.sheets/com.google.android.apps.docs.app.NewMainProxyActivity'
+docsActivity='com.google.android.apps.docs.editors.docs/com.google.android.apps.docs.app.NewMainProxyActivity'
+operaActivity='com.opera.mini.native/com.opera.mini.android.Browser'
+firefoxActivity='org.mozilla.firefox/org.mozilla.firefox.App'
+homeActivity='com.google.android.googlequicksearchbox/com.google.android.launcher.GEL'
+
+function showUsage {
+ echo "$0: unrecognized option: $1"
+ echo; echo "Usage: $0 [options]"
+ echo "-e : stop on error"
+ echo "-i iterations"
+ echo "-n : keep trace files"
+ echo "-o output file"
+ echo "-s device : adb device"
+ echo "-t trace categories"
+ echo "-g : generate activity strings"
+}
+
+DEVICE=unknown
+
+# handle args
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ (-d) DEVICE=$2; shift;;
+ (-e) stoponerror=1;;
+ (-n) savetmpfiles=1;;
+ (-t) tracecategories=$2; shift;;
+ (-i) iterations=$2; shift;;
+ (-o) output=$2; shift;;
+ (-v) verbose=1;;
+ (-nz) compress=0;;
+ (-s) deviceName=$2; shift;;
+ (-g) generateActivities=1;;
+ (--) ;;
+ (*)
+ chk1=$(functions 2>/dev/null)
+ chk2=$(typeset -F 2>/dev/null)
+
+ if echo $chk1 $chk2 | grep -q processLocalOption; then
+ if ! processLocalOption "$1" "$2"; then
+ shift
+ fi
+ else
+ showUsage $1
+ exit 1
+ fi;;
+ esac
+ shift
+done
+
+# check if running on a device
+if ls /etc/* 2>/dev/null | grep -q android.hardware; then
+ ADB=""
+ compress=0
+ isOnDevice=1
+else
+ # do a throw-away adb in case the server is out-of-date
+ adb devices -l 2>&1 >/dev/null
+
+ if [ -z "$deviceName" ]; then
+ devInfo=$(adb devices -l | grep -v ^List | head -1)
+ else
+ devInfo=$(adb devices -l | grep $deviceName)
+ fi
+ set -- $devInfo
+ if [ -z $1 ]; then
+ echo Error: could not find device $deviceName
+ exit 1
+ fi
+ deviceName=$1
+ ADB="adb -s $deviceName shell "
+ DEVICE=$(echo $4 | sed 's/product://')
+ isOnDevice=0
+fi
+
+# default values if not set by options or calling script
+appList=${appList:=$dfltAppList}
+savetmpfiles=${savetmpfiles:=0}
+stoponerror=${stoponerror:=0}
+verbose=${verbose:=0}
+compress=${compress:=1}
+iterations=${iterations:=5}
+tracecategories=${tracecategories:=$dflttracecategories}
+ADB=${ADB:=""}
+output=${output:="./out"}
+
+# clear the output file
+> $output
+
+# ADB commands
+AM_FORCE_START="${ADB}am start -W -S"
+AM_START="${ADB}am start -W"
+AM_START_NOWAIT="${ADB}am start"
+AM_STOP="${ADB}am force-stop"
+AM_LIST="${ADB}am stack list"
+WHO="${ADB}whoami"
+INPUT="${ADB}input"
+PS="${ADB}ps"
+
+function vout {
+ # debug output enabled by -v
+ if [ $verbose -gt 0 ]; then
+ echo DEBUG: $* >&2
+ echo DEBUG: $* >&2 >> $output
+ fi
+}
+
+function findtimestamp {
+ # extract timestamp from atrace log entry
+ while [ "$2" != "" -a "$2" != "tracing_mark_write" ]
+ do
+ shift
+ done
+ echo $1
+}
+
+function computeTimeDiff {
+ # Compute time diff given: startSeconds startNs endSeconds endNS
+
+ # strip leading zeros
+ startS=$(expr 0 + $1)
+ endS=$(expr 0 + $3)
+ if [ "$2" = N ]; then
+ startNs=0
+ endNs=0
+ else
+ startNs=$(expr 0 + $2)
+ endNs=$(expr 0 + $4)
+ fi
+
+ ((startMs=startS*1000 + startNs/1000000))
+ ((endMs=endS*1000 + endNs/1000000))
+ ((diff=endMs-startMs))
+ echo $diff
+}
+
+function log2msec {
+ in=$1
+ in=${in:=0.0}
+ set -- $(echo $in | tr . " ")
+ # shell addition via (( )) doesn't like leading zeroes in msecs
+ # field so remove leading zeroes
+ msecfield=$(expr 0 + $2)
+
+ ((msec=$1*1000000+msecfield))
+ ((msec=msec/1000))
+ echo $msec
+}
+
+function getStartTime {
+ # extract event indicating beginning of start sequence
+ # a) look for a "launching" event indicating start from scratch
+ # b) look for another activity getting a pause event
+ _app=$1
+ traceout=$2
+ ret=0
+ s=$(grep "Binder.*tracing_mark_write.*launching" $traceout 2>/dev/null | head -1| tr [\(\)\[\]\r:] " ")
+ if [ -z "$s" ]; then
+ s=$(grep activityPause $traceout | head -1 2>/dev/null| tr [\(\)\[\]\r:] " ")
+ else
+ vout $_app was restarted!
+ ret=1
+ fi
+ vout STARTLOG: $s
+ log2msec $(findtimestamp $s)
+ return $ret
+}
+
+function getEndTime {
+ # extract event indicating end of start sequence. We use the
+ # first surfaceflinger event associated with the target activity
+ _app=$1
+ traceout=$2
+ f=$(grep "surfaceflinger.*tracing_mark_write.*$_app" $traceout 2>/dev/null |
+ grep -v Starting | head -1 | tr [\(\)\[\]\r:] " ")
+ if [ -z "$f" ]; then
+ # Hmm. sf symbols may not be there... get the pid
+ pid=$(${ADB}pidof /system/bin/surfaceflinger | tr "[\r]" "[ ]")
+ f=$(grep " <...>-$pid.*tracing_mark_write.*$_app" $traceout 2>/dev/null |
+ grep -v Starting | head -1 | tr [\(\)\[\]\r:] " ")
+ fi
+ vout ENDLOG: $f
+ log2msec $(findtimestamp $f)
+}
+
+function resetJankyFrames {
+ _gfxapp=$1
+ _gfxapp=${app:="com.android.systemui"}
+ ${ADB}dumpsys gfxinfo $_gfxapp reset 2>&1 >/dev/null
+}
+
+function getJankyFrames {
+ _gfxapp=$1
+ _gfxapp=${_gfxapp:="com.android.systemui"}
+
+ # Note: no awk or sed on devices so have to do this
+ # purely with bash
+ total=0
+ janky=0
+ latency=0
+ ${ADB}dumpsys gfxinfo $_gfxapp | tr "\r" " " | egrep "9[059]th| frames" | while read line
+ do
+ if echo $line | grep -q "Total frames"; then
+ set -- $line
+ total=$4
+ elif echo $line | grep -q "Janky frames"; then
+ set -- $line
+ janky=$3
+ elif echo $line | grep -q "90th"; then
+ set -- $(echo $line | tr m " ")
+ l90=$3
+ elif echo $line | grep -q "95th"; then
+ set -- $(echo $line | tr m " ")
+ l95=$3
+ elif echo $line | grep -q "99th"; then
+ set -- $(echo $line | tr m " ")
+ l99=$3
+ echo $total $janky $l90 $l95 $l99
+ break
+ fi
+ done
+}
+
+function checkForDirectReclaim {
+ # look for any reclaim events in atrace output
+ _app=$1
+ traceout=$2
+ if grep -qi reclaim $traceout; then
+ return 1
+ fi
+ return 0
+}
+
+function startInstramentation {
+ # Called at beginning of loop. Turn on instramentation like atrace
+ vout start instramentation $(date)
+ echo =============================== >> $output
+ echo Before iteration >> $output
+ echo =============================== >> $output
+ ${ADB}cat /proc/meminfo 2>&1 >> $output
+ ${ADB}dumpsys meminfo 2>&1 >> $output
+ if [ "$user" = root ]; then
+ vout ${ADB}atrace -b 32768 --async_start $tracecategories
+ ${ADB}atrace -b 32768 --async_start $tracecategories >> $output
+ echo >> $output
+ fi
+}
+
+function stopInstramentation {
+ if [ "$user" = root ]; then
+ vout ${ADB}atrace --async_stop
+ ${ADB}atrace --async_stop > /dev/null
+ fi
+}
+
+function stopAndDumpInstramentation {
+ # Called at beginning of loop. Turn on instramentation like atrace
+ vout stop instramentation $(date)
+ echo =============================== >> $output
+ echo After iteration >> $output
+ echo =============================== >> $output
+ ${ADB}cat /proc/meminfo 2>&1 >> $output
+ ${ADB}dumpsys meminfo 2>&1 >> $output
+ if [ "$user" = root ]; then
+ traceout=$1
+ traceout=${traceout:=$output}
+ echo =============================== >> $traceout
+ echo TRACE >> $traceout
+ echo =============================== >> $traceout
+ if [ $compress -gt 0 ]; then
+ tmpTrace=./tmptrace.$$
+ UNCOMPRESS=$CMDDIR/atrace-uncompress.py
+ > $tmpTrace
+ zarg="-z"
+ ${ADB}atrace -z -b 32768 --async_dump >> $tmpTrace
+ python $UNCOMPRESS $tmpTrace >> $traceout
+ rm -f $tmpTrace
+ else
+ ${ADB}atrace $zarg -b 32768 --async_dump >> $traceout
+ fi
+ vout ${ADB}atrace $zarg --async_dump
+ vout ${ADB}atrace --async_stop
+ ${ADB}atrace --async_stop > /dev/null
+ fi
+}
+
+function getActivityName {
+ cmd="actName=\$${1}Activity"
+ eval $cmd
+ echo $actName
+}
+
+function getPackageName {
+ set -- $(getActivityName $1 | tr "[/]" "[ ]")
+ echo $1
+}
+
+function startActivityFromPackage {
+ if [ "$1" = home ]; then
+ doKeyevent HOME
+ echo 0
+ return 0
+ fi
+ vout $AM_START_NOWAIT -p "$(getPackageName $1)" -c android.intent.category.LAUNCHER -a android.intent.action.MAIN
+ $AM_START_NOWAIT -p "$(getPackageName $1)" -c android.intent.category.LAUNCHER -a android.intent.action.MAIN 2>&1
+ echo 0
+}
+
+function startActivity {
+ if [ "$1" = home ]; then
+ doKeyevent HOME
+ echo 0
+ return 0
+ elif [ "$1" = chrome ]; then
+ if [ "$DEVICE" = volantis ]; then
+ vout $AM_START_NOWAIT -p "$(getPackageName $1)" http://www.theverge.com
+ $AM_START_NOWAIT -p "$(getPackageName $1)" http://www.theverge.com > /dev/null
+ set -- 0 0
+ else
+ vout $AM_START -p "$(getPackageName $1)" http://www.theverge.com
+ set -- $($AM_START -p "$(getPackageName $1)" http://www.theverge.com | grep ThisTime)
+ fi
+ else
+ vout $AM_START "$(getActivityName $1)"
+ set -- $($AM_START "$(getActivityName $1)" | grep ThisTime)
+ fi
+ echo $2 | tr "[\r]" "[\n]"
+}
+
+function forceStartActivity {
+ if [ "$1" = chrome ]; then
+ vout $AM_START -p "$(getPackageName $1)" http://www.theverge.com
+ set -- $($AM_FORCE_START -p "$(getPackageName $1)" http://www.theverge.com | grep ThisTime)
+ else
+ vout $AM_FORCE_START "$(getActivityName $1)"
+ set -- $($AM_FORCE_START "$(getActivityName $1)" | grep ThisTime)
+ fi
+ echo $2 | tr "[\r]" "[\n]"
+}
+
+function checkActivity {
+ # requires root
+ actName="$(getActivityName $1)"
+ $AM_LIST | grep $actName
+}
+
+#function stopActivity {
+# vout $AM_STOP $(getActivityName $1)
+# $AM_STOP $(getActivityName $1)
+#}
+
+function doSwipe {
+ vout ${ADB}input swipe $*
+ ${ADB}input swipe $*
+}
+
+function doTap {
+ vout ${ADB}input tap $*
+ ${ADB}input tap $*
+}
+
+function doKeyevent {
+ vout $INPUT keyevent $*
+ $INPUT keyevent $*
+}
+
+function checkIsRunning {
+ p=$1
+ shift
+ if ! $PS | grep $p | grep -qv grep; then
+ handleError $*: $p is not running
+ exit 1
+ fi
+}
+
+function checkStartTime {
+ vout checkStartTime $1 v $2
+ if [ -z "$2" ]; then
+ echo false
+ return 2
+ fi
+ if [ "$1" -gt "$2" ]; then
+ echo false
+ return 1
+ fi
+ echo true
+ return 0
+}
+
+function handleError {
+ echo Error: $*
+ stopAndDumpInstramentation
+ if [ $stoponerror -gt 0 ]; then
+ exit 1
+ fi
+}
+
+user=root
+if ${ADB}ls /data 2>/dev/null | grep -q "Permission denied"; then
+ user=shell
+fi
+vout User is $user
+
+if [ $generateActivities -gt 0 ]; then
+ if [ $isOnDevice -gt 0 ]; then
+ echo Error: cannot generate activity list when run on device
+ exit 1
+ fi
+ echo Generating activities...
+ for app in $appList
+ do
+ startActivityFromPackage $app 2>&1 > /dev/null
+ act=$(${ADB}am stack list | grep $(getPackageName $app) | sed -e 's/\r//' | head -1 | awk '{ print $2; }')
+ eval "${app}Activity=$act"
+ echo "ACTIVITY: $app --> $(getActivityName $app)"
+ done
+fi
+
--- /dev/null
+# Script to automate the following sequence:
+# - Open Feedly
+# - Open an article
+# - Scroll to bottome
+# - Open the same article in Chrome
+# - Scroll the article
+# - Back to Feely (should still be in memory)
+# - Home screen
+# ---- repeat ----
+#
+# Currently works on volantis only (verticle orientation)
+#
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+. $CMDDIR/defs.sh
+
+case "$DEVICE" in
+(volantis)
+ echo volantis...
+ feedlyArticle="500 700"
+ feedlyOptions="1480 100"
+ feedlyBrowserSelect="1350 650"
+ feedlyArticleSwipeUp="700 700 700 50 50"
+ feedlyArticleSwipeDown="700 200 700 700 50"
+ chromeSwipe="700 700 700 50 50"
+ ;;
+(shamu|*)
+ echo shamu...
+ feedlyArticle="676 500"
+ feedlyOptions="1327 207"
+ feedlyBrowserSelect="1278 1191"
+ feedlyArticleSwipeUp="700 1847 700 400 50"
+ feedlyArticleSwipeDown="700 400 700 1847 50"
+ chromeSwipe="700 1847 700 400 50"
+ ;;
+(hammerhead|*)
+ echo "Error: No feedly screen geometry information available for $DEVICE"
+ exit 1;;
+esac
+
+feedlySwitchToTime=600
+
+# start feedly, if not installed, error out
+t=$(forceStartActivity feedly)
+checkIsRunning feedly "initial start of feedly"
+echo Feedly start time = ${t}ms
+
+# start chrome, if not installed, error out
+t=$(forceStartActivity chrome)
+checkIsRunning chrome "initial start of chrome"
+echo Chrome start time = ${t}ms
+sleep 1
+
+feedlyStartTimes=0
+
+cur=1
+while [ $cur -le $iterations ]
+do
+ echo =======================================
+ echo Iteration $cur of $iterations
+ echo =======================================
+ startInstramentation
+ t=$(startActivity feedly)
+ if [ $(checkStartTime "$t" $feedlySwitchToTime) != true ]; then
+ handleError Feedly took too long to start: $t v $feedlySwitchToTime: $?
+ # for now, not fatal
+ # exit 1
+ fi
+ sleep 2
+ ((feedlyStartTimes=feedlyStartTimes+t))
+ echo feedly started in ${t}ms
+ checkIsRunning chrome "switch back to feedly"
+ checkIsRunning googlequicksearchbox "switch back to feedly"
+
+ # click on first article
+ doTap $feedlyArticle
+ sleep 2
+
+ # scroll through article
+ doSwipe $feedlyArticleSwipeUp
+ sleep 5
+ checkIsRunning chrome "feedly swipe"
+ checkIsRunning googlequicksearchbox "feedly swipe"
+
+ # scroll back to top
+ doSwipe $feedlyArticleSwipeDown
+ sleep 2
+
+ # switch to chrome
+ # 1. click on menu bar
+ doTap $feedlyOptions
+ sleep 1
+ # 2. click on browser
+ doTap $feedlyBrowserSelect
+ sleep 10
+
+ checkIsRunning feedly "switch to chrome"
+ checkIsRunning googlequicksearchbox "switch to chrome"
+
+ # Now we're back in chrome, swipe to bottom of article
+ doSwipe $chromeSwipe
+ sleep 2
+ checkIsRunning feedly "swiped chrome"
+ stopInstramentation
+ ((cur=cur+1))
+done
+((feedlyAve=feedlyStartTimes/iterations))
+echo Avg start times: feedly: ${feedlyAve}ms
+
+doKeyevent HOME
--- /dev/null
+#
+# Script to start a set of apps, switch to recents and fling it back and forth.
+# For each iteration, Total frames and janky frames are reported.
+#
+# Options are described below.
+#
+# Works for volantis, shamu, and hammerhead. Can be pushed and executed on
+# the device.
+#
+iterations=10
+startapps=1
+capturesystrace=0
+
+function processLocalOption {
+ ret=0
+ case "$1" in
+ (-N) startapps=0;;
+ (-A) unset appList;;
+ (-L) appList=$2; shift; ret=1;;
+ (-T) capturesystrace=1;;
+ (*)
+ echo "$0: unrecognized option: $1"
+ echo; echo "Usage: $0 [options]"
+ echo "-A : use all known applications"
+ echo "-L applist : list of applications"
+ echo " default: $appList"
+ echo "-N : no app startups, just fling"
+ echo "-g : generate activity strings"
+ echo "-i iterations"
+ echo "-T : capture systrace on each iteration"
+ exit 1;;
+ esac
+ return $ret
+}
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+. $CMDDIR/defs.sh
+
+case $DEVICE in
+(shamu|hammerhead)
+ flingtime=300
+ downCount=2
+ upCount=6
+ UP="70 400 70 100 $flingtime"
+ DOWN="70 100 70 400 $flingtime";;
+(bullhead)
+ flingtime=200
+ downCount=5
+ upCount=5
+ UP="500 1200 500 550 $flingtime"
+ DOWN="500 550 500 1200 $flingtime";;
+(volantis)
+ flingtime=400
+ downCount=5
+ upCount=6
+ UP="70 400 70 70 $flingtime"
+ DOWN="70 70 70 400 $flingtime";;
+(*)
+ echo "Error: No display information available for $DEVICE"
+ exit 1;;
+esac
+
+doKeyevent HOME
+if [ $startapps -gt 0 ]; then
+
+ # start a bunch of apps
+ for app in $appList
+ do
+ echo Starting $app ...
+ t=$(startActivity $app)
+ done
+fi
+
+function swipe {
+ count=0
+ while [ $count -lt $2 ]
+ do
+ doSwipe $1
+ ((count=count+1))
+ done
+}
+
+cur=1
+frameSum=0
+jankSum=0
+latency90Sum=0
+latency95Sum=0
+latency99Sum=0
+
+echo Fling recents...
+doKeyevent HOME
+sleep 0.5
+resetJankyFrames
+
+while [ $cur -le $iterations ]
+do
+ if [ $capturesystrace -gt 0 ]; then
+ ${ADB}atrace --async_start -z -c -b 16000 freq gfx view idle sched
+ fi
+ doKeyevent APP_SWITCH
+ sleep 0.5
+ swipe "$DOWN" $downCount
+ sleep 1
+ swipe "$UP" $upCount
+ sleep 1
+ swipe "$DOWN" $downCount
+ sleep 1
+ swipe "$UP" $upCount
+ sleep 1
+ if [ $capturesystrace -gt 0 ]; then
+ ${ADB}atrace --async_dump -z -c -b 16000 freq gfx view idle sched > trace.${cur}.out
+ fi
+ doKeyevent HOME
+ sleep 0.5
+
+ set -- $(getJankyFrames)
+ totalDiff=$1
+ jankyDiff=$2
+ latency90=$3
+ latency95=$4
+ latency99=$5
+ if [ ${totalDiff:=0} -eq 0 ]; then
+ echo Error: could not read frame info with \"dumpsys gfxinfo\"
+ exit 1
+ fi
+
+ ((frameSum=frameSum+totalDiff))
+ ((jankSum=jankSum+jankyDiff))
+ ((latency90Sum=latency90Sum+latency90))
+ ((latency95Sum=latency95Sum+latency95))
+ ((latency99Sum=latency99Sum+latency99))
+ if [ "$totalDiff" -eq 0 ]; then
+ echo Error: no frames detected. Is the display off?
+ exit 1
+ fi
+ ((jankPct=jankyDiff*100/totalDiff))
+ resetJankyFrames
+
+ echo Frames: $totalDiff latency: $latency90/$latency95/$latency99 Janks: $jankyDiff\(${jankPct}%\)
+ ((cur=cur+1))
+done
+doKeyevent HOME
+((aveJankPct=jankSum*100/frameSum))
+((aveJanks=jankSum/iterations))
+((aveFrames=frameSum/iterations))
+((aveLatency90=latency90Sum/iterations))
+((aveLatency95=latency95Sum/iterations))
+((aveLatency99=latency99Sum/iterations))
+echo AVE: Frames: $aveFrames latency: $aveLatency90/$aveLatency95/$aveLatency99 Janks: $aveJanks\(${aveJankPct}%\)
--- /dev/null
+# Script to start a set of apps in order and then in each iteration
+# switch the focus to each one. For each iteration, the time to start
+# the app is reported as measured using atrace events and via am ThisTime.
+# The output also reports if applications are restarted (eg, killed by
+# LMK since previous iteration) or if there were any direct reclaim
+# events.
+#
+# Variation: the "-T" option skips all of the atrace instramentation and
+# attempts to start the apps as quickly as possible.
+#
+# Example 1: start all default apps. 2 iterations
+#
+# ./systemapps.sh -i 2
+#
+# Example 2: just start chrome, feedly, and the home screen in a loop
+#
+# ./systemapps.sh -L "chrome feedly home" -i 5
+#
+# Example 3: just start the default apps as quickly as possible
+#
+# ./systemapps.sh -T
+#
+# Other options are described below.
+#
+iterations=1
+tracecategories="gfx view am input memreclaim"
+totaltimetest=0
+forcecoldstart=0
+waitTime=3.0
+
+appList="gmail hangouts chrome youtube play home"
+
+function processLocalOption {
+ ret=0
+ case "$1" in
+ (-A) unset appList;;
+ (-F) forcecoldstart=1;;
+ (-L) appList=$2; shift; ret=1;;
+ (-T) totaltimetest=1;;
+ (-W) waitTime=$2; shift; ret=1;;
+ (*)
+ echo "$0: unrecognized option: $1"
+ echo; echo "Usage: $0 [options]"
+ echo "-A : use all known applications"
+ echo "-F : force cold-start for all apps"
+ echo "-L applist : list of applications"
+ echo " default: $appList"
+ echo "-T : total time to start all apps"
+ echo "-W : time to wait between apps"
+ echo "-g : generate activity strings"
+ echo "-i iterations"
+ echo "-n : keep trace files"
+ echo "-o output file"
+ echo "-s : stop on error"
+ echo "-t trace categories"
+ exit 1;;
+ esac
+ return $ret
+}
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+. $CMDDIR/defs.sh
+
+tmpTraceOutBase=./tmptrace
+
+if [ $user != "root" -a $totaltimetest -eq 0 ]; then
+ handleError Must be root on device
+ exit 1
+fi
+doKeyevent HOME
+
+function computeStats {
+ label=$1
+ t=$2
+ restart=$3
+ reclaim=$4
+ frames=$5
+ janks=$6
+ l90=$7
+ l95=$8
+ l99=$9
+ curMax=$(eval "echo \$${label}max")
+ curMax=${curMax:=0}
+ curMin=$(eval "echo \$${label}min")
+ curMin=${curMin:=100000}
+ curSum=$(eval "echo \$${label}sum")
+ curSum=${curSum:=0}
+ curRestart=$(eval "echo \$${label}restart")
+ curRestart=${curRestart:=0}
+ curReclaim=$(eval "echo \$${label}reclaim")
+ curReclaim=${curReclaim:=0}
+ curFrames=$(eval "echo \$${label}frames")
+ curFrames=${curFrames:=0}
+ curJanks=$(eval "echo \$${label}janks")
+ curJanks=${curJanks:=0}
+ cur90=$(eval "echo \$${label}90")
+ cur90=${cur90:=0}
+ cur95=$(eval "echo \$${label}95")
+ cur95=${cur95:=0}
+ cur99=$(eval "echo \$${label}99")
+ cur99=${cur99:=0}
+ if [ $curMax -lt $t ]; then
+ eval "${label}max=$t"
+ fi
+ if [ $curMin -gt $t ]; then
+ eval "${label}min=$t"
+ fi
+ ((curSum=curSum+t))
+ eval "${label}sum=$curSum"
+
+ ((curRestart=curRestart+${restart:=0}))
+ eval "${label}restart=$curRestart"
+ ((curReclaim=curReclaim+${reclaim:=0}))
+ eval "${label}reclaim=$curReclaim"
+ ((curFrames=curFrames+${frames:=0}))
+ eval "${label}frames=$curFrames"
+ ((curJanks=curJanks+${janks:=0}))
+ eval "${label}janks=$curJanks"
+ ((cur90=cur90+${l90:=0}))
+ eval "${label}90=$cur90"
+ ((cur95=cur95+${l95:=0}))
+ eval "${label}95=$cur95"
+ ((cur99=cur99+${l99:=0}))
+ eval "${label}99=$cur99"
+}
+function getStats {
+ label=$1
+ echo $(eval "echo \$${label}max") $(eval "echo \$${label}min") $(eval "echo \$${label}sum") \
+ $(eval "echo \$${label}restart") $(eval "echo \$${label}reclaim") \
+ $(eval "echo \$${label}frames") $(eval "echo \$${label}janks") \
+ $(eval "echo \$${label}90") $(eval "echo \$${label}95") $(eval "echo \$${label}99")
+}
+
+cur=1
+totaltime=0
+startTimestamp=$(date +"%s %N")
+
+while [ $cur -le $iterations ]
+do
+ if [ $iterations -gt 1 ]; then
+ echo =========================================
+ echo Iteration $cur of $iterations
+ echo =========================================
+ fi
+ if [ $iterations -gt 1 -o $cur -eq 1 ]; then
+ if [ $totaltimetest -eq 0 ]; then
+ printf "%-6s %7s(ms) %6s(ms) %s %s %s %s\n" App Time AmTime Restart DirReclaim Jank Latency
+ fi
+ fi
+
+ appnum=-1
+ for app in $appList
+ do
+ vout Starting $app...
+ ((appnum=appnum+1))
+ loopTimestamp=$(date +"%s %N")
+ resetJankyFrames
+ resetJankyFrames $(getPackageName $app)
+ if [ $totaltimetest -eq 0 ]; then
+ tmpTraceOut="$tmpTraceOutBase-$app.out"
+ >$tmpTraceOut
+ startInstramentation
+ else
+ if [ $appnum -eq 0 ]; then
+ printf "%-8s %5s(ms) %3s(ms) %s %s\n" App Start Iter Jank Latency
+ fi
+ fi
+ if [ $forcecoldstart -eq 0 ]; then
+ t=$(startActivity $app)
+ else
+ t=$(forceStartActivity $app)
+ fi
+
+ # let app finish drawing before checking janks
+ sleep $waitTime
+ set -- $(getJankyFrames $(getPackageName $app))
+ frames=$1
+ janks=$2
+ l90=$3
+ l95=$4
+ l99=$5
+ set -- $(getJankyFrames)
+ systemFrames=$1
+ systemJanks=$2
+ s90=$3
+ s95=$4
+ s99=$5
+ ((frames=frames+systemFrames))
+ ((janks=janks+systemJanks))
+ ((l90=l90+s90))
+ ((l95=l95+s95))
+ ((l99=l99+s99))
+
+ loopEndTimestamp=$(date +"%s %N")
+ diffTime=$(computeTimeDiff $loopTimestamp $loopEndTimestamp)
+
+ if [ $frames -eq 0 ]; then
+ janks=0
+ jankPct=0
+ else
+ ((jankPct=100*janks/frames))
+ fi
+ if [ $totaltimetest -gt 0 ]; then
+ # Note: using %f since %d doesn't work correctly
+ # when running on lollipop
+ printf "%-10s %5.0f %5.0f %4.0f(%2.0f%%) %2.0f/%2.0f/%2.0f\n" $app $t $diffTime $janks $jankPct $l90 $l95 $l99
+ ((totaltime=totaltime+t))
+ continue
+ else
+ stopAndDumpInstramentation $tmpTraceOut
+ actName=$(getActivityName $app)
+ pkgName=$(getPackageName $app)
+ stime=$(getStartTime $actName $tmpTraceOut)
+ relaunch=$?
+ etime=$(getEndTime $pkgName $tmpTraceOut)
+ ((tdiff=$etime-$stime))
+ if [ $etime -eq 0 -o $stime -eq 0 ]; then
+ handleError $app : could not compute start time stime=$stime etime=$etime
+ # use AmTime so statistics make sense
+ tdiff=$t
+ fi
+ checkForDirectReclaim $actName $tmpTraceOut
+ directReclaim=$?
+
+ printf "%-12s %5d %5d %5d %5d %5d(%d%%) %d/%d/%d\n" "$app" "$tdiff" "$t" "$relaunch" "$directReclaim" "$janks" "$jankPct" $l90 $l95 $l99
+ computeStats "$app" "$tdiff" "$relaunch" "$directReclaim" "$frames" "$janks" $l90 $l95 $l99
+
+ if [ $savetmpfiles -eq 0 ]; then
+ rm -f $tmpTraceOut
+ fi
+ fi
+ done
+ ((cur=cur+1))
+done
+endTimestamp=$(date +"%s %N")
+diffTime=$(computeTimeDiff $startTimestamp $endTimestamp)
+if [ $totaltimetest -gt 0 ]; then
+ printf "%-10s %5.0f %5.0f\n" TOTAL $totaltime $diffTime
+fi
+
+if [ $iterations -gt 1 -a $totaltimetest -eq 0 ]; then
+ echo
+ echo =========================================
+ printf "Stats after $iterations iterations:\n"
+ echo =========================================
+ printf "%-6s %7s(ms) %6s(ms) %6s(ms) %s %s %s %s\n" App Max Ave Min Restart DirReclaim Jank Latency
+ for app in $appList
+ do
+ set -- $(getStats $app)
+ sum=$3
+ ((ave=sum/iterations))
+ frames=$6
+ janks=$7
+ l90=$8
+ l95=$9
+ l99=${10}
+ ((ave90=l90/iterations))
+ ((ave95=l95/iterations))
+ ((ave99=l99/iterations))
+ ((jankPct=100*janks/frames))
+ printf "%-12s %5d %5d %5d %5d %5d %5d(%d%%) %d/%d/%d\n" $app $1 $ave $2 $4 $5 $janks $jankPct $ave90 $ave95 $ave99
+ done
+fi
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.ECPrivateKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.util.encoders.Base64;
public class Utils {
ID_TO_ALG = new HashMap<String, String>();
ALG_TO_ID = new HashMap<String, String>();
+ ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA");
+ ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA");
+ ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA");
ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA");
+ ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId());
+ ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId());
+ ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId());
ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId());
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
}
}
- private static String getSignatureAlgorithm(Key key) {
- if ("RSA".equals(key.getAlgorithm())) {
+ private static String getSignatureAlgorithm(Key key) throws Exception {
+ if ("EC".equals(key.getAlgorithm())) {
+ int curveSize;
+ KeyFactory factory = KeyFactory.getInstance("EC");
+
+ if (key instanceof PublicKey) {
+ ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class);
+ curveSize = spec.getParams().getCurve().getField().getFieldSize();
+ } else if (key instanceof PrivateKey) {
+ ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class);
+ curveSize = spec.getParams().getCurve().getField().getFieldSize();
+ } else {
+ throw new InvalidKeySpecException();
+ }
+
+ if (curveSize <= 256) {
+ return "SHA256withECDSA";
+ } else if (curveSize <= 384) {
+ return "SHA384withECDSA";
+ } else {
+ return "SHA512withECDSA";
+ }
+ } else if ("RSA".equals(key.getAlgorithm())) {
return "SHA256withRSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
}
}
- static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) {
+ static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception {
String id = ALG_TO_ID.get(getSignatureAlgorithm(key));
if (id == null) {
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.lang.Math;
import java.lang.Process;
import java.lang.Runtime;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
import java.security.PublicKey;
-import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class VerityVerifier {
+ private ArrayList<Integer> hashBlocksLevel;
+ private byte[] hashTree;
+ private byte[] rootHash;
+ private byte[] salt;
+ private byte[] signature;
+ private byte[] table;
+ private File image;
+ private int blockSize;
+ private int hashBlockSize;
+ private int hashOffsetForData;
+ private int hashSize;
+ private int hashTreeSize;
+ private long hashStart;
+ private long imageSize;
+ private MessageDigest digest;
+
private static final int EXT4_SB_MAGIC = 0xEF53;
private static final int EXT4_SB_OFFSET = 0x400;
private static final int EXT4_SB_OFFSET_MAGIC = EXT4_SB_OFFSET + 0x38;
private static final int EXT4_SB_OFFSET_LOG_BLOCK_SIZE = EXT4_SB_OFFSET + 0x18;
private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_LO = EXT4_SB_OFFSET + 0x4;
private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_HI = EXT4_SB_OFFSET + 0x150;
+ private static final int MINCRYPT_OFFSET_MODULUS = 0x8;
+ private static final int MINCRYPT_OFFSET_EXPONENT = 0x208;
+ private static final int MINCRYPT_MODULUS_SIZE = 0x100;
+ private static final int MINCRYPT_EXPONENT_SIZE = 0x4;
+ private static final int VERITY_FIELDS = 10;
private static final int VERITY_MAGIC = 0xB001B001;
private static final int VERITY_SIGNATURE_SIZE = 256;
private static final int VERITY_VERSION = 0;
+ public VerityVerifier(String fname) throws Exception {
+ digest = MessageDigest.getInstance("SHA-256");
+ hashSize = digest.getDigestLength();
+ hashBlocksLevel = new ArrayList<Integer>();
+ hashTreeSize = -1;
+ openImage(fname);
+ readVerityData();
+ }
+
+ /**
+ * Reverses the order of bytes in a byte array
+ * @param value Byte array to reverse
+ */
+ private static byte[] reverse(byte[] value) {
+ for (int i = 0; i < value.length / 2; i++) {
+ byte tmp = value[i];
+ value[i] = value[value.length - i - 1];
+ value[value.length - i - 1] = tmp;
+ }
+
+ return value;
+ }
+
/**
* Converts a 4-byte little endian value to a Java integer
* @param value Little endian integer to convert
*/
- public static int fromle(int value) {
+ private static int fromle(int value) {
byte[] bytes = ByteBuffer.allocate(4).putInt(value).array();
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
* Converts a 2-byte little endian value to Java a integer
* @param value Little endian short to convert
*/
- public static int fromle(short value) {
+ private static int fromle(short value) {
return fromle(value << 16);
}
/**
+ * Reads a 2048-bit RSA public key saved in mincrypt format, and returns
+ * a Java PublicKey for it.
+ * @param fname Name of the mincrypt public key file
+ */
+ private static PublicKey getMincryptPublicKey(String fname) throws Exception {
+ try (RandomAccessFile key = new RandomAccessFile(fname, "r")) {
+ byte[] binaryMod = new byte[MINCRYPT_MODULUS_SIZE];
+ byte[] binaryExp = new byte[MINCRYPT_EXPONENT_SIZE];
+
+ key.seek(MINCRYPT_OFFSET_MODULUS);
+ key.readFully(binaryMod);
+
+ key.seek(MINCRYPT_OFFSET_EXPONENT);
+ key.readFully(binaryExp);
+
+ BigInteger modulus = new BigInteger(1, reverse(binaryMod));
+ BigInteger exponent = new BigInteger(1, reverse(binaryExp));
+
+ RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ return factory.generatePublic(spec);
+ }
+ }
+
+ /**
* Unsparses a sparse image into a temporary file and returns a
* handle to the file
* @param fname Path to a sparse image file
*/
- public static RandomAccessFile openImage(String fname) throws Exception {
- File tmp = File.createTempFile("system", ".raw");
- tmp.deleteOnExit();
+ private void openImage(String fname) throws Exception {
+ image = File.createTempFile("system", ".raw");
+ image.deleteOnExit();
Process p = Runtime.getRuntime().exec("simg2img " + fname +
- " " + tmp.getAbsoluteFile());
+ " " + image.getAbsoluteFile());
p.waitFor();
if (p.exitValue() != 0) {
throw new IllegalArgumentException("Invalid image: failed to unsparse");
}
-
- return new RandomAccessFile(tmp, "r");
}
/**
}
/**
- * Reads and validates verity metadata, and check the signature against the
+ * Calculates the size of the verity hash tree based on the image size
+ */
+ private int calculateHashTreeSize() {
+ if (hashTreeSize > 0) {
+ return hashTreeSize;
+ }
+
+ int totalBlocks = 0;
+ int hashes = (int) (imageSize / blockSize);
+
+ hashBlocksLevel.clear();
+
+ do {
+ hashBlocksLevel.add(0, hashes);
+
+ int hashBlocks =
+ (int) Math.ceil((double) hashes * hashSize / hashBlockSize);
+
+ totalBlocks += hashBlocks;
+
+ hashes = hashBlocks;
+ } while (hashes > 1);
+
+ hashTreeSize = totalBlocks * hashBlockSize;
+ return hashTreeSize;
+ }
+
+ /**
+ * Parses the verity mapping table and reads the hash tree from
+ * the image file
+ * @param img Handle to the image file
+ * @param table Verity mapping table
+ */
+ private void readHashTree(RandomAccessFile img, byte[] table)
+ throws Exception {
+ String tableStr = new String(table);
+ String[] fields = tableStr.split(" ");
+
+ if (fields.length != VERITY_FIELDS) {
+ throw new IllegalArgumentException("Invalid image: unexpected number of fields "
+ + "in verity mapping table (" + fields.length + ")");
+ }
+
+ String hashVersion = fields[0];
+
+ if (!"1".equals(hashVersion)) {
+ throw new IllegalArgumentException("Invalid image: unsupported hash format");
+ }
+
+ String alg = fields[7];
+
+ if (!"sha256".equals(alg)) {
+ throw new IllegalArgumentException("Invalid image: unsupported hash algorithm");
+ }
+
+ blockSize = Integer.parseInt(fields[3]);
+ hashBlockSize = Integer.parseInt(fields[4]);
+
+ int blocks = Integer.parseInt(fields[5]);
+ int start = Integer.parseInt(fields[6]);
+
+ if (imageSize != (long) blocks * blockSize) {
+ throw new IllegalArgumentException("Invalid image: size mismatch in mapping "
+ + "table");
+ }
+
+ rootHash = DatatypeConverter.parseHexBinary(fields[8]);
+ salt = DatatypeConverter.parseHexBinary(fields[9]);
+
+ hashStart = (long) start * blockSize;
+ img.seek(hashStart);
+
+ int treeSize = calculateHashTreeSize();
+
+ hashTree = new byte[treeSize];
+ img.readFully(hashTree);
+ }
+
+ /**
+ * Reads verity data from the image file
+ */
+ private void readVerityData() throws Exception {
+ try (RandomAccessFile img = new RandomAccessFile(image, "r")) {
+ imageSize = getMetadataPosition(img);
+ img.seek(imageSize);
+
+ int magic = fromle(img.readInt());
+
+ if (magic != VERITY_MAGIC) {
+ throw new IllegalArgumentException("Invalid image: verity metadata not found");
+ }
+
+ int version = fromle(img.readInt());
+
+ if (version != VERITY_VERSION) {
+ throw new IllegalArgumentException("Invalid image: unknown metadata version");
+ }
+
+ signature = new byte[VERITY_SIGNATURE_SIZE];
+ img.readFully(signature);
+
+ int tableSize = fromle(img.readInt());
+
+ table = new byte[tableSize];
+ img.readFully(table);
+
+ readHashTree(img, table);
+ }
+ }
+
+ /**
+ * Reads and validates verity metadata, and checks the signature against the
* given public key
- * @param img File handle to the image file
* @param key Public key to use for signature verification
*/
- public static boolean verifyMetaData(RandomAccessFile img, PublicKey key)
+ public boolean verifyMetaData(PublicKey key)
throws Exception {
- img.seek(getMetadataPosition(img));
- int magic = fromle(img.readInt());
+ return Utils.verify(key, table, signature,
+ Utils.getSignatureAlgorithmIdentifier(key));
+ }
+
+ /**
+ * Hashes a block of data using a salt and checks of the results are expected
+ * @param hash The expected hash value
+ * @param data The data block to check
+ */
+ private boolean checkBlock(byte[] hash, byte[] data) {
+ digest.reset();
+ digest.update(salt);
+ digest.update(data);
+ return Arrays.equals(hash, digest.digest());
+ }
+
+ /**
+ * Verifies the root hash and the first N-1 levels of the hash tree
+ */
+ private boolean verifyHashTree() throws Exception {
+ int hashOffset = 0;
+ int dataOffset = hashBlockSize;
- if (magic != VERITY_MAGIC) {
- throw new IllegalArgumentException("Invalid image: verity metadata not found");
+ if (!checkBlock(rootHash, Arrays.copyOfRange(hashTree, 0, hashBlockSize))) {
+ System.err.println("Root hash mismatch");
+ return false;
}
- int version = fromle(img.readInt());
+ for (int level = 0; level < hashBlocksLevel.size() - 1; level++) {
+ int blocks = hashBlocksLevel.get(level);
+
+ for (int i = 0; i < blocks; i++) {
+ byte[] hashBlock = Arrays.copyOfRange(hashTree,
+ hashOffset + i * hashSize,
+ hashOffset + i * hashSize + hashSize);
+
+ byte[] dataBlock = Arrays.copyOfRange(hashTree,
+ dataOffset + i * hashBlockSize,
+ dataOffset + i * hashBlockSize + hashBlockSize);
- if (version != VERITY_VERSION) {
- throw new IllegalArgumentException("Invalid image: unknown metadata version");
+ if (!checkBlock(hashBlock, dataBlock)) {
+ System.err.printf("Hash mismatch at tree level %d, block %d\n", level, i);
+ return false;
+ }
+ }
+
+ hashOffset = dataOffset;
+ hashOffsetForData = dataOffset;
+ dataOffset += blocks * hashBlockSize;
}
- byte[] signature = new byte[VERITY_SIGNATURE_SIZE];
- img.readFully(signature);
+ return true;
+ }
- int tableSize = fromle(img.readInt());
+ /**
+ * Validates the image against the hash tree
+ */
+ public boolean verifyData() throws Exception {
+ if (!verifyHashTree()) {
+ return false;
+ }
- byte[] table = new byte[tableSize];
- img.readFully(table);
+ try (RandomAccessFile img = new RandomAccessFile(image, "r")) {
+ byte[] dataBlock = new byte[blockSize];
+ int hashOffset = hashOffsetForData;
- return Utils.verify(key, table, signature,
- Utils.getSignatureAlgorithmIdentifier(key));
+ for (int i = 0; (long) i * blockSize < imageSize; i++) {
+ byte[] hashBlock = Arrays.copyOfRange(hashTree,
+ hashOffset + i * hashSize,
+ hashOffset + i * hashSize + hashSize);
+
+ img.readFully(dataBlock);
+
+ if (!checkBlock(hashBlock, dataBlock)) {
+ System.err.printf("Hash mismatch at block %d\n", i);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Verifies the integrity of the image and the verity metadata
+ * @param key Public key to use for signature verification
+ */
+ public boolean verify(PublicKey key) throws Exception {
+ return (verifyMetaData(key) && verifyData());
}
public static void main(String[] args) throws Exception {
- if (args.length != 2) {
- System.err.println("Usage: VerityVerifier <sparse.img> <certificate.x509.pem>");
+ Security.addProvider(new BouncyCastleProvider());
+ PublicKey key = null;
+
+ if (args.length == 3 && "-mincrypt".equals(args[1])) {
+ key = getMincryptPublicKey(args[2]);
+ } else if (args.length == 2) {
+ X509Certificate cert = Utils.loadPEMCertificate(args[1]);
+ key = cert.getPublicKey();
+ } else {
+ System.err.println("Usage: VerityVerifier <sparse.img> <certificate.x509.pem> | -mincrypt <mincrypt_key>");
System.exit(1);
}
- Security.addProvider(new BouncyCastleProvider());
-
- X509Certificate cert = Utils.loadPEMCertificate(args[1]);
- PublicKey key = cert.getPublicKey();
- RandomAccessFile img = openImage(args[0]);
+ VerityVerifier verifier = new VerityVerifier(args[0]);
try {
- if (verifyMetaData(img, key)) {
+ if (verifier.verify(key)) {
System.err.println("Signature is VALID");
System.exit(0);
- } else {
- System.err.println("Signature is INVALID");
}
} catch (Exception e) {
e.printStackTrace(System.err);