app-launcher is a App Launch Perf Analysis tool.

app-launcher is an automated tool that automates the launching of
selected Apps (hundreds of times each) and collects system metrics
like CPU util, storage device util, launch times, context switches,
branch mispredicts etc. This is very useful for A/B compares of system
changes in how they impact App Launch. This code was under
google_experimental and was used by various members who found it very
useful, so moving it under system/extras (under userdebug).

Test: I (and others) have used app launcher on multiple occasions over
several months. And comparison of metrics reported by app-launcher are
consistent with metrics reported by other tools, for these tests.

Change-Id: I8b469b4165035a958b155cfffc93341cddcc444c
Signed-off-by: Mohan Srinivasan <srmohan@google.com>
diff --git a/app-launcher/Android.mk b/app-launcher/Android.mk
new file mode 100644
index 0000000..755b1d3
--- /dev/null
+++ b/app-launcher/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := computestatsf.c
+LOCAL_MODULE := computestatsf
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := computestats.c
+LOCAL_MODULE := computestats
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_EXECUTABLES := app-launcher
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+include $(BUILD_HOST_PREBUILT)
diff --git a/app-launcher/README b/app-launcher/README
new file mode 100644
index 0000000..cca58aa
--- /dev/null
+++ b/app-launcher/README
@@ -0,0 +1,76 @@
+		Instructions to Run (and modify) app-launcher script
+		----------------------------------------------------
+
+Introduction: app-launcher is a script that launches apps many times,
+measures various system metrics, computes basic stats for the metrics
+and reports that stats.
+
+Launching app-launcher :
+app-launcher -a|-b|-u [-v] num-iterations
+-a:Run on all cores
+-b:Run only big cores
+-c:pagecached. Don't drop pagecache before each launch (not default)
+-h:Dump help menu'
+-u:user experience, no change to cpu/gpu frequencies or governors'
+-v : Optional, Verbose mode, prints stats on a lot of metrics.
+num-iterations : Must be >= 100 to get statistically valid data.
+
+Note, under -a|-b, we lock the CPU and GPU frequencies.
+
+Apps Supported :
+On phone, these 4 apps are launched
+Chrome
+Camera
+Maps
+Youtube
+
+On Fugu (Google TV), these 3 apps are launched
+YouTube
+Games
+Music
+
+To add new apps, launch app manually and grep for package name +
+activity name in logcat and add these to the launch_phone_apps()
+function.
+
+Adding support for new Devices to app-launcher :
+There are a few bits of code needed to do this.
+1) Add a new cpufreq_<device> routine to fix the CPU/GPU frequencies
+as desired.
+2) Add logic that checks the $model obtained and check against your device.
+   (a) Then add code to call your cpufreq_<device> routine there
+   (b) (Optional) Add code to get the /system block device pathname. This is
+   only needed if you wan to get storage block device (/system) data.
+
+Adding new Metrics to app-launcher :
+You can modify the way simpleperf is used in the script to collect
+different metrics, but that will require a change to getstats() to
+parse the output as necessary. Adding new storage stats or other stats
+collected from /proc (or /sys) is definitely possible, but code needs
+to be written for that - modeled after the disk_stats_before/after
+functions.
+
+Notes :
+
+Here are the commands to launch/stop the various Apps of interest. The
+way to find the package and activity for the app of interest is to
+launch the app and then grep for it in logcat to find the
+package+activity and use that in am start.
+
+Chrome :
+adb shell 'simpleperf stat -a am start -W -n com.android.chrome/com.google.android.apps.chrome.Main'
+adb shell 'am force-stop com.android.chrome'
+
+Camera :
+adb shell 'simpleperf stat -a am start -W -n com.google.android.GoogleCamera/com.android.camera.CameraActivity'
+adb shell 'am force-stop com.google.android.GoogleCamera'
+
+Maps :
+adb shell 'simpleperf stat -a am start -W -n com.google.android.apps.maps/com.google.android.maps.MapsActivity'
+adb shell 'am force-stop com.google.android.apps.maps'
+
+Youtube :
+adb shell 'am start -W -n com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity'
+adb shell 'am force-stop com.google.android.youtube'
+
+
diff --git a/app-launcher/app-launcher b/app-launcher/app-launcher
new file mode 100755
index 0000000..44e62f8
--- /dev/null
+++ b/app-launcher/app-launcher
@@ -0,0 +1,461 @@
+#!/bin/sh
+
+parseoptions() {
+    verbose=false
+    user_experience=false
+    little_cores_off=false
+    iterations=0
+    pagecached=false
+
+    while [ $# -gt 1 ]
+    do
+	case $1 in
+	    -a)
+		;;
+	    -b)
+		little_cores_off=true
+		;;
+	    -c)
+		pagecached=true
+		;;
+	    -h)
+		usage
+		;;
+	    -u)
+		user_experience=true
+		;;
+	    -v)
+		verbose=true
+		;;
+	    *)
+		usage
+		;;
+	    esac
+	shift
+    done
+
+    iterations=$1
+    if [ $iterations -lt 100 ]; then
+	usage
+    fi
+}
+
+getstats () {
+    infile=$1
+    app=$2
+    echo "Data for $app :"
+
+    # Chrome related Hack alert :
+    # For Chrome launches, "TotalTime" is the right metric to measure. That is closer
+    # to what the user seems Chrome launch time as. So special case Chrome here.
+
+    if [ $app == Chrome ]; then
+	launch_time_string="TotalTime"
+    else
+	launch_time_string="ThisTime"
+    fi
+
+    # From Activity Manager
+    echo "Launch Time :"
+    fgrep $launch_time_string $infile | awk '{print $2}' | computestats
+
+    # Data from simpleperf
+    echo "cpu-cycles :"
+    fgrep cpu-cycles $infile | awk '{print $1}' | sed s/,//g | computestats
+
+    # CPU util% Data from /proc/stat
+    echo "cpu-util% :"
+    fgrep 'Total CPU util' $infile | awk '{print $5}' | computestatsf
+    echo "user-cpu-util% :"
+    fgrep 'User CPU util' $infile | awk '{print $5}' | computestatsf
+    echo "sys-cpu-util% (incl hardirq/softirq) :"
+    fgrep 'Sys CPU util' $infile | awk '{print $5}' | computestatsf
+
+    if [ $verbose == true ]; then
+	echo "instructions : "
+	fgrep instructions $infile | awk '{print $1}' | sed s/,//g | computestats
+
+	echo "cycles per instruction : "
+	fgrep instructions $infile | awk '{print $4}' | sed s/,//g | computestatsf
+
+	echo "branch-misses : "
+	fgrep branch-misses $infile | awk '{print $1}' | sed s/,//g | computestats
+
+	echo "context-switches : "
+	fgrep context-switches $infile | awk '{print $1}' | sed s/,//g | computestats
+
+	echo "page-faults : "
+	fgrep page-faults $infile | awk '{print $1}' | sed s/,//g | computestats
+    fi
+
+    if [ $system_bdev_set == true ]; then
+	# (Storage) Data from /proc we've collected
+	echo "KB read for $system_block_device blkdev :"
+	fgrep KB $infile | grep system | awk '{print $5}' | computestats
+
+	echo "iowait% :"
+	fgrep IOwait $infile | awk '{print $3}' | computestatsf
+
+	echo "Device util% for $system_block_device blkdev :"
+	fgrep 'Device util' $infile | awk '{print $4}' | computestatsf
+    fi
+}
+
+cpufreq_volantis() {
+    echo "Setting Governor to performance"
+    if [ $little_cores_off == true ]; then
+        echo "Cannot turn off Little cores on $model"
+        exit 1
+    fi
+    i=0
+    num_cores=2
+    while [ $i -lt  $num_cores ]
+    do
+        adb shell "echo performance  > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_g\
+overnor"
+        adb shell "echo 2499000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_fr\
+eq"
+        i=`expr $i + 1`
+    done
+    # Lock the GPU frequencies
+    echo -n 852000000 > /d/clock/override.gbus/rate
+    echo -n 1 > /d/clock/override.gbus/state
+}
+
+cpufreq_fugu() {
+    echo "Setting Governor to performance"
+    if [ $little_cores_off == true ]; then
+	echo "Cannot turn off Little cores on $model"
+	exit 1
+    fi
+    i=0
+    num_cores=4
+    while [ $i -lt  $num_cores ]
+    do
+	adb shell "echo performance  > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+	adb shell "echo 1833000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+	i=`expr $i + 1`
+    done
+}
+
+cpufreq_marlin_sailfish () {
+    echo "Setting Governor to performance"
+    # GPU Governor and Frequency
+    adb shell 'echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor'
+    adb shell 'echo 624000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq'
+    if [ $little_cores_off == true ]; then
+	# Disable Little Cores, force app to run on big cores
+	echo "Disabling Little Cores"
+	adb shell 'echo 0 > /sys/devices/system/cpu/cpu0/online'
+	adb shell 'echo 0 > /sys/devices/system/cpu/cpu1/online'
+    else
+	echo "Enabling All Cores"
+	adb shell 'echo 1 > /sys/devices/system/cpu/cpu0/online'
+	adb shell 'echo 1 > /sys/devices/system/cpu/cpu1/online'
+	adb shell 'echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'
+	adb shell 'echo 1996800 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq'
+	# cpu1 needed ?
+	adb shell 'echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor'
+	adb shell 'echo 1996800 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq'
+    fi
+    # Set Governor to performance, up scaling_max_frequency to highest
+    adb shell 'echo performance  > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor'
+    # Only necessary to set max_freq on cpu2, cpu3 is in same cluster and will
+    # automatically get the same settings
+    adb shell 'echo 2150400 > /sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq'
+}
+
+cpufreq_angler () {
+    echo "Setting Governor and Frequency"
+    # GPU Governor and Frequency
+    adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+    adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+    adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+    adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+    if [ $little_cores_off == true ]; then
+	# Disable Little Cores, force app to run on big cores
+	echo "Disabling Little Cores"
+	i=0
+	num_cores=4
+	while [ $i -lt $num_cores ]
+	do
+	    adb shell "echo 0 > /sys/devices/system/cpu/cpu$i/online"
+	    i=`expr $i + 1`
+	done
+    else
+	echo "Enabling All Cores"
+	# Enable Little cores here, set governor to performance
+	# Lock frequency of little cores
+	i=0
+	num_cores=4
+	while [ $i -lt $num_cores ]
+	do
+	    adb shell "echo 1 > /sys/devices/system/cpu/cpu$i/online"
+	    adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+	    # Lock frequency of little cores
+	    adb shell "echo 1555200 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+	    i=`expr $i + 1`
+	done
+    fi
+    i=4
+    num_cores=8
+    while [ $i -lt $num_cores ]
+    do
+	adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+	# Lock frequency of big cores
+	adb shell "echo 1958400 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+	i=`expr $i + 1`
+    done
+}
+
+#
+# This strange bit of logic is needed to get the underlying block devices for /system
+# for Marlin/Sailfish
+#
+get_marlin_sailfish_devnames () {
+    # This bit of code required to get the block dev for /system and /vendor
+    # Suffix can be _a or _b, depending on what the active /system partition is
+#    suffix=`adb shell getprop ro.boot.slot_suffix`
+    # Get the blockdevice using the suffix we got above
+#    system_block_device=`adb shell ls -l /dev/block/platform/soc/*ufs*/by-name/system$suffix | awk '{ print $10 }' `
+    # Vendor is more straightforward, but we don't use it right now
+#    vendor_block_device=`adb shell df /vendor | grep -v Filesystem | awk '{print $1}' `
+    # finally extract the last component of the absolute device pathname we got above
+#    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+#    vendor_block_device=`echo $vendor_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    system_bdev_set=true
+#   For now, hardcode sda for Marlin/Sailfish block device
+#   XXX - We'll get stats for entire device
+    system_block_device=sda
+    echo Block Device $system_block_device
+}
+
+get_angler_devnames () {
+    # Get the underlying bdev from the "by-name" mapping
+    system_block_device=`adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep system | awk '{ print $10 }' `
+    # extract the last component of the absolute device pathname we got above
+    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    # vendor is unused right now, but get the bdev anyway in case we decide to use it
+    # Get the underlying bdev from the "by-name" mapping
+    vendor_block_device=`adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep vendor | awk '{ print $10 }' `
+    # extract the last component of the absolute device pathname we got above
+   vendor_block_device=`echo $vendor_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+   system_bdev_set=true
+}
+
+get_fugu_devnames () {
+    system_block_device=`adb shell ls -l /dev/block/by-name/system | awk '{ print $10 }' `
+    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    system_bdev_set=true
+}
+
+get_volantis_devnames () {
+    # Hardcoding all of the mmcblk0 device for now
+    system_block_device=mmcblk0
+    system_bdev_set=true
+}
+
+system_stats_before() {
+    if [ $system_bdev_set == true ]; then
+	# Get BEFORE read stats for /system
+	adb shell 'cat /proc/diskstats' | grep -w $system_block_device > /tmp/$model-system
+	BEFORE_RD_IOS_SYSTEM=`awk '{ print $4 }' /tmp/$model-system`
+	BEFORE_RD_SECTORS_SYSTEM=`awk '{ print $6 }' /tmp/$model-system`
+	# iowait% computation
+	adb shell 'cat /proc/stat' | grep -w cpu > /tmp/procstat
+	user_ticks_before=`awk '{ print ($2 + $3) }' /tmp/procstat`
+	sys_ticks_before=`awk '{ print ($4 + $7 + $8) }' /tmp/procstat`
+	cpubusy_ticks_before=`awk '{ print ($2 + $3 + $4 + $7 + $8) }' /tmp/procstat`
+	iowait_ticks_before=`awk '{ print $6 }' /tmp/procstat`
+	total_ticks_before=`awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }' /tmp/procstat`
+	# Device util% computation
+	# Note hz=100, so multiplying uptime (in seconds) by 100, gives us
+	# the uptime in hz.
+	adb shell 'cat /proc/uptime' > /tmp/uptime
+	uptime_before_hz=`awk '{ print ($1 * 100) }' /tmp/uptime`
+	# Note that the device (busy) ticks is in ms. Since hz=100, dividing
+	# device (busy) ticks by 10, gives us this in the correct ticks units
+	device_util_before_hz=`awk '{ print ($13 / 10) }' /tmp/$model-system`
+    fi
+}
+
+system_stats_after() {
+    if [ $system_bdev_set == true ]; then
+	# Get AFTER read stats for /system
+	adb shell 'cat /proc/diskstats' | grep -w $system_block_device > /tmp/$model-system
+	AFTER_RD_IOS_SYSTEM=`awk '{ print $4 }' /tmp/$model-system`
+	AFTER_RD_SECTORS_SYSTEM=`awk '{ print $6 }' /tmp/$model-system`
+	# iowait% computation
+	adb shell 'cat /proc/stat' | grep -w cpu > /tmp/procstat
+	user_ticks_after=`awk '{ print ($2 + $3) }' /tmp/procstat`
+	sys_ticks_after=`awk '{ print ($4 + $7 + $8) }' /tmp/procstat`
+	cpubusy_ticks_after=`awk '{ print ($2 + $3 + $4 + $7 + $8) }' /tmp/procstat`
+	iowait_ticks_after=`awk '{ print $6 }' /tmp/procstat`
+	total_ticks_after=`awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }' /tmp/procstat`
+	# Device util% computation
+	# Note hz=100, so multiplying uptime (in seconds) by 100, gives us
+	# the uptime in hz.
+	adb shell 'cat /proc/uptime' > /tmp/uptime
+	uptime_after_hz=`awk '{ print ($1 * 100) }' /tmp/uptime`
+	# Note that the device (busy) ticks is in ms. Since hz=100, dividing
+	# device (busy) ticks by 10, gives us this in the correct ticks units
+	device_util_after_hz=`awk '{ print ($13 / 10) }' /tmp/$model-system`
+    fi
+}
+
+system_stats_delta() {
+    if [ $system_bdev_set == true ]; then
+	# Sectors to KB
+	READ_KB_SYSTEM=`expr $AFTER_RD_SECTORS_SYSTEM - $BEFORE_RD_SECTORS_SYSTEM`
+	READ_KB_SYSTEM=`expr $READ_KB_SYSTEM / 2`
+	echo Read IOs /system = `expr $AFTER_RD_IOS_SYSTEM - $BEFORE_RD_IOS_SYSTEM`
+	echo Read KB /system = $READ_KB_SYSTEM
+	echo $iowait_ticks_before $iowait_ticks_after $total_ticks_before $total_ticks_after | awk '{ printf "IOwait = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }'
+	echo $device_util_before_hz $device_util_after_hz $uptime_before_hz $uptime_after_hz | awk '{ printf "Device util% = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }'
+	echo $user_ticks_after $user_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "User CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+	echo $sys_ticks_after $sys_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Sys CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+	echo $cpubusy_ticks_after $cpubusy_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Total CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+    fi
+}
+
+launch_app() {
+    package=$1
+    activity=$2
+    adb shell "am force-stop $package"
+    sleep 1
+
+    i=0
+    while [ $i -lt $iterations ]
+    do
+	if [ $pagecached == false ]; then
+	    adb shell 'echo 3 > /proc/sys/vm/drop_caches'
+	fi
+	# The -W argument to am start forces am start to wait till the launch completes.
+	# The -S argument forces it to kill any existing app that is running first
+	# eg. adb shell 'am start -W -S -n com.android.chrome/com.google.android.apps.chrome.Main'
+	system_stats_before
+	adb shell "simpleperf stat -a am start -W -n $package/$activity"
+	system_stats_after
+	system_stats_delta
+	sleep 1
+	adb shell "am force-stop $package"
+	sleep 1
+	i=`expr $i + 1`
+    done
+}
+
+launch_fugu_apps() {
+    launch_app com.google.android.youtube.tv com.google.android.apps.youtube.tv.activity.TvGuideActivity > youtube-$model
+    getstats youtube-$model YouTube
+    launch_app com.google.android.play.games com.google.android.gms.games.pano.activity.PanoGamesOnboardHostActivity > games-$model
+    getstats games-$model Games
+    launch_app com.google.android.music com.android.music.activitymanagement.TopLevelActivity > music-$model
+    getstats music-$model Music
+}
+
+launch_phone_apps() {
+    launch_app com.android.chrome com.google.android.apps.chrome.Main > chrome-$model
+    getstats chrome-$model Chrome
+    launch_app com.google.android.GoogleCamera com.android.camera.CameraActivity > camera-$model
+    getstats camera-$model Camera
+    launch_app com.google.android.apps.maps com.google.android.maps.MapsActivity > maps-$model
+    getstats maps-$model Maps
+#   launch_app com.google.android.youtube com.google.android.apps.youtube.app.WatchWhileActivity > youtube-$model
+#   getstats youtube-$model YouTube
+}
+
+usage() {
+    echo 'Usage: app-launcher [-c|-v] -a|-b|-u num-iterations'
+    echo 'where num-iterations >= 100'
+    echo '-v (optional) for verbose stats dump'
+    echo '-a|-b|-u required:'
+    echo '        -a:all cores'
+    echo '        -b:only big cores'
+    echo '        -c:pagecached. Do not drop pagecache before each launch (not default)'
+    echo '        -h:Dump this help menu'
+    echo '        -u:user experience, no change to cpu/gpu frequencies or governors'
+    echo '        -a/-b locks CPU/GPU freqs to max, performance governor, thermal/perfd off'
+    echo '        -u runs with default device configs, as users would see it'
+    exit 1
+}
+
+#
+# The main() part of the script follows :
+#
+
+if [ $# -lt 2 ]; then
+    usage
+fi
+
+which computestats > /dev/null
+if [ $? != 0 ]; then
+    echo "ERROR: Please add computestats utiliy to your PATH"
+    exit 1
+fi
+
+which computestatsf > /dev/null
+if [ $? != 0 ]; then
+    echo "Error: Please add computestatsf utility to your PATH"
+    exit 1
+fi
+
+parseoptions $@
+
+adb root && sleep 2
+
+if [ $user_experience == false ]; then
+    # Important to stop the thermal-engine to prevent throttling while test is running
+    # and stop perfd
+    adb shell 'stop thermal-engine'
+    adb shell 'stop perfd'
+else
+    echo "User Experience: Default Configs. No changes to cpufreq settings"
+fi
+
+model=`adb shell getprop ro.product.name`
+# Releases are inconsistent with various trailing characters, remove them all
+model=`echo $model | sed 's/[ \t\r\n]*$//' `
+
+echo Found $model Device
+
+system_bdev_set=false
+case $model in
+    marlin | sailfish)
+        if [ $user_experience == false ]; then
+            cpufreq_marlin_sailfish
+        fi
+	get_marlin_sailfish_devnames
+        ;;
+    angler)
+        if [ $user_experience == false ]; then
+            cpufreq_angler
+        fi
+        get_angler_devnames
+        ;;
+    fugu)
+        if [ $user_experience == false ]; then
+            cpufreq_fugu
+        fi
+        get_fugu_devnames
+        ;;
+    volantis | volantisg)
+        if [ $user_experience == false ]; then
+            cpufreq_volantis
+        fi
+        get_volantis_devnames
+        ;;
+    *)
+        echo Unknown Device $model
+        exit 1
+        ;;
+esac
+
+
+#
+# launch each app in turn
+#
+if [ $model == "fugu" ]; then
+    launch_fugu_apps
+else # Phone Apps
+    launch_phone_apps
+fi
diff --git a/app-launcher/computestats.c b/app-launcher/computestats.c
new file mode 100644
index 0000000..e081e06
--- /dev/null
+++ b/app-launcher/computestats.c
@@ -0,0 +1,118 @@
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+char *pname;
+char *in_file;
+
+#define DATA_COUNT	(1024*1024)
+u_int64_t data_items[DATA_COUNT];
+
+int num_data_items = 0;
+
+#define BUFSIZE		1024
+char in_buf[BUFSIZE];
+
+static int
+compare_long(const void *p1, const void *p2)
+{
+	u_int64_t val1 = *(u_int64_t *)p1;
+	u_int64_t val2 = *(u_int64_t *)p2;
+
+	if (val1 == val2)
+		return 0;
+	if (val1 < val2)
+		return -1;
+	return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+	FILE *in_fp;
+	u_int64_t sum_x = 0;
+	u_int64_t sum_sq_x = 0;
+	u_int64_t mean;
+	double std_dev;
+	int i;
+	int one_sd = 0;
+	int two_sd = 0;
+	int three_sd = 0;
+	double one_sd_low, one_sd_high;
+	double two_sd_low, two_sd_high;
+	double three_sd_low, three_sd_high;
+
+	pname = argv[0];
+	if (argc == 1)
+		in_fp = stdin;
+	else {
+		in_file = argv[1];
+		in_fp = fopen(in_file, "r");
+	}
+	while (fgets(in_buf, BUFSIZE, in_fp)) {
+		if (num_data_items == DATA_COUNT) {
+			fprintf(stderr,
+				"DATA overflow, increase size of data_items array\n");
+			exit(1);
+		}
+		sscanf(in_buf, "%ju", &data_items[num_data_items]);
+#if 0
+		printf("%lu\n", data_items[num_data_items++]);
+#endif
+		num_data_items++;
+	}
+	if (num_data_items == 0) {
+		fprintf(stderr, "Empty input file ?\n");
+		exit(1);
+	}
+#if 0
+	printf("Total items %lu\n", num_data_items);
+#endif
+	for (i = 0 ; i < num_data_items ; i++) {
+		sum_x += data_items[i];
+		sum_sq_x += data_items[i] * data_items[i];
+	}
+	mean = sum_x / num_data_items;
+	printf("\tMean %lu\n", mean);
+	std_dev = sqrt((sum_sq_x / num_data_items) - (mean * mean));
+	printf("\tStd Dev %.2f (%.2f%% of mean)\n",
+	       std_dev, (std_dev * 100.0) / mean);
+	one_sd_low = mean - std_dev;
+	one_sd_high = mean + std_dev;
+	two_sd_low = mean - (2 * std_dev);
+	two_sd_high = mean + (2 * std_dev);
+	three_sd_low = mean - (3 * std_dev);
+	three_sd_high = mean + (3 * std_dev);
+	for (i = 0 ; i < num_data_items ; i++) {
+		if (data_items[i] >= one_sd_low &&
+		    data_items[i] <= one_sd_high)
+			one_sd++;
+		if (data_items[i] >= two_sd_low &&
+		    data_items[i] <= two_sd_high)
+			two_sd++;
+		if (data_items[i] >= three_sd_low &&
+			 data_items[i] <= three_sd_high)
+			three_sd++;
+	}
+	printf("\tWithin 1 SD %.2f%%\n",
+	       ((double)one_sd * 100) / num_data_items);
+	printf("\tWithin 2 SD %.2f%%\n",
+	       ((double)two_sd * 100) / num_data_items);
+	printf("\tWithin 3 SD %.2f%%\n",
+	       ((double)three_sd* 100) / num_data_items);
+	printf("\tOutside 3 SD %.2f%%\n",
+	       ((double)(num_data_items - three_sd) * 100) / num_data_items);
+	/* Sort the data to get percentiles */
+	qsort(data_items, num_data_items, sizeof(u_int64_t), compare_long);
+	printf("\t50th percentile %lu\n", data_items[num_data_items / 2]);
+	printf("\t75th percentile %lu\n", data_items[(3 * num_data_items) / 4]);
+	printf("\t90th percentile %lu\n", data_items[(9 * num_data_items) / 10]);
+	printf("\t99th percentile %lu\n", data_items[(99 * num_data_items) / 100]);
+}
+
diff --git a/app-launcher/computestatsf.c b/app-launcher/computestatsf.c
new file mode 100644
index 0000000..2ec0b9d
--- /dev/null
+++ b/app-launcher/computestatsf.c
@@ -0,0 +1,118 @@
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+char *pname;
+char *in_file;
+
+#define DATA_COUNT	(1024*1024)
+double data_items[DATA_COUNT];
+
+int num_data_items = 0;
+
+#define BUFSIZE		1024
+char in_buf[BUFSIZE];
+
+static int
+compare_double(const void *p1, const void *p2)
+{
+	double val1 = *(u_int64_t *)p1;
+	double val2 = *(u_int64_t *)p2;
+
+	if (val1 == val2)
+		return 0;
+	if (val1 < val2)
+		return -1;
+	return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+	FILE *in_fp;
+	double sum_x = 0;
+	double sum_sq_x = 0;
+	double mean;
+	double std_dev;
+	int i;
+	int one_sd = 0;
+	int two_sd = 0;
+	int three_sd = 0;
+	double one_sd_low, one_sd_high;
+	double two_sd_low, two_sd_high;
+	double three_sd_low, three_sd_high;
+
+	pname = argv[0];
+	if (argc == 1)
+		in_fp = stdin;
+	else {
+		in_file = argv[1];
+		in_fp = fopen(in_file, "r");
+	}
+	while (fgets(in_buf, BUFSIZE, in_fp)) {
+		if (num_data_items == DATA_COUNT) {
+			fprintf(stderr,
+				"DATA overflow, increase size of data_items array\n");
+			exit(1);
+		}
+		sscanf(in_buf, "%lf", &data_items[num_data_items]);
+#if 0
+		printf("%lf\n", data_items[num_data_items]);
+#endif
+		num_data_items++;
+	}
+	if (num_data_items == 0) {
+		fprintf(stderr, "Empty input file ?\n");
+		exit(1);
+	}
+#if 0
+	printf("Total items %lu\n", num_data_items);
+#endif
+	for (i = 0 ; i < num_data_items ; i++) {
+		sum_x += data_items[i];
+		sum_sq_x += data_items[i] * data_items[i];
+	}
+	mean = sum_x / num_data_items;
+	printf("\tMean %.4f\n", mean);
+	std_dev = sqrt((sum_sq_x / num_data_items) - (mean * mean));
+	printf("\tStd Dev %.4f (%.4f%% of mean)\n",
+	       std_dev, (std_dev * 100.0) / mean);
+	one_sd_low = mean - std_dev;
+	one_sd_high = mean + std_dev;
+	two_sd_low = mean - (2 * std_dev);
+	two_sd_high = mean + (2 * std_dev);
+	three_sd_low = mean - (3 * std_dev);
+	three_sd_high = mean + (3 * std_dev);
+	for (i = 0 ; i < num_data_items ; i++) {
+		if (data_items[i] >= one_sd_low &&
+		    data_items[i] <= one_sd_high)
+			one_sd++;
+		if (data_items[i] >= two_sd_low &&
+		    data_items[i] <= two_sd_high)
+			two_sd++;
+		if (data_items[i] >= three_sd_low &&
+			 data_items[i] <= three_sd_high)
+			three_sd++;
+	}
+	printf("\tWithin 1 SD %.2f%%\n",
+	       ((double)one_sd * 100) / num_data_items);
+	printf("\tWithin 2 SD %.2f%%\n",
+	       ((double)two_sd * 100) / num_data_items);
+	printf("\tWithin 3 SD %.2f%%\n",
+	       ((double)three_sd* 100) / num_data_items);
+	printf("\tOutside 3 SD %.2f%%\n",
+	       ((double)(num_data_items - three_sd) * 100) / num_data_items);
+	/* Sort the data to get percentiles */
+	qsort(data_items, num_data_items, sizeof(u_int64_t), compare_double);
+	printf("\t50th percentile %lf\n", data_items[num_data_items / 2]);
+	printf("\t75th percentile %lf\n", data_items[(3 * num_data_items) / 4]);
+	printf("\t90th percentile %lf\n", data_items[(9 * num_data_items) / 10]);
+	printf("\t99th percentile %lf\n", data_items[(99 * num_data_items) / 100]);
+}
+