OSDN Git Service

Scripts to automate simple workloads for testing
authorTodd Kjos <tkjos@google.com>
Wed, 20 May 2015 19:47:00 +0000 (12:47 -0700)
committerTodd Kjos <tkjos@google.com>
Wed, 20 May 2015 19:51:26 +0000 (12:51 -0700)
DO NOT MERGE (already in master)

The initial set of scripts:
recentsfling.sh : start a set of apps, switch to recents and fling
systemapps.sh : start a set of apps, and then loop to move them
                to the forground, checking whether the app had
                been LMK'd or required direct reclaims
feedly-chrome.sh : automate a feedly, chrome
                workflow making sure both stay in memory

Change-Id: I40f0862623ea0963bb0eaed3a585b41b69bbbbc7

tests/workloads/atrace-uncompress.py [new file with mode: 0644]
tests/workloads/capture.sh [new file with mode: 0755]
tests/workloads/defs.sh [new file with mode: 0755]
tests/workloads/feedly-chrome.sh [new file with mode: 0755]
tests/workloads/recentfling.sh [new file with mode: 0755]
tests/workloads/systemapps.sh [new file with mode: 0755]

diff --git a/tests/workloads/atrace-uncompress.py b/tests/workloads/atrace-uncompress.py
new file mode 100644 (file)
index 0000000..5efb698
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# 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()
diff --git a/tests/workloads/capture.sh b/tests/workloads/capture.sh
new file mode 100755 (executable)
index 0000000..721fe8c
--- /dev/null
@@ -0,0 +1,62 @@
+# 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
+devInfo=$(adb devices -l | grep -v ^List | head -1)
+
+set -- $devInfo
+echo devInfo=$devInfo
+
+DEVICE=$(echo $4 | sed 's/product://')
+
+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)
+       # 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
diff --git a/tests/workloads/defs.sh b/tests/workloads/defs.sh
new file mode 100755 (executable)
index 0000000..838d04f
--- /dev/null
@@ -0,0 +1,434 @@
+# 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/com.google.android.apps.chrome.ChromeTabbedActivity'
+chromeLActivity='com.android.chrome/com.google.android.apps.chrome.document.DocumentActivity'
+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 {
+       ${ADB}dumpsys gfxinfo $1 reset 2>&1 >/dev/null
+}
+
+function getJankyFrames {
+       if [ -z "$ADB" ]; then
+               # Note: no awk or sed on devices so have to do this
+               # purely with bash
+               total=0
+               janky=0
+               /system/bin/dumpsys gfxinfo | grep " frames" | while read line
+               do
+                       if echo $line | grep -q "Total frames"; then
+                               set -- $line
+                               ((total=total+$4))
+                       elif echo $line | grep -q "Janky frames"; then
+                               set -- $line
+                               ((janky=janky+$3))
+                       fi
+                       # Note: no tail, awk, or sed on 5.x so get final
+                       # sum via most recently written file
+                       echo $total $janky > ./janky.$$
+               done
+               cat ./janky.$$
+               rm -f ./janky.$$
+       else
+               ${ADB}dumpsys gfxinfo $1 | sed -e 's/\r//' | awk '
+                       BEGIN { total=0; janky=0; }
+                       /Total frames/ { total+=$4; }
+                       /Janky frames/ {  janky+=$3; }
+                       END { printf "%d %d\n", total, janky; }'
+       fi
+}
+
+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" = chromeL ]; then
+               vout $AM_START -p "$(getPackageName $1)" http://www.theverge.com
+               set -- $($AM_START -p "$(getPackageName $1)" http://www.theverge.com | grep ThisTime)
+       else
+               vout $AM_START "$(getActivityName $1)"
+               set -- $($AM_START "$(getActivityName $1)" | grep ThisTime)
+       fi
+       echo $2 | tr "[\r]" "[\n]"
+}
+
+function forceStartActivity {
+       if [ "$1" = chromeL ]; then
+               # force start doesn't work for chrome (hangs on startup)
+               startActivity $*
+               return 0
+       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
+
diff --git a/tests/workloads/feedly-chrome.sh b/tests/workloads/feedly-chrome.sh
new file mode 100755 (executable)
index 0000000..4c7002f
--- /dev/null
@@ -0,0 +1,111 @@
+# 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
diff --git a/tests/workloads/recentfling.sh b/tests/workloads/recentfling.sh
new file mode 100755 (executable)
index 0000000..68fdb2f
--- /dev/null
@@ -0,0 +1,132 @@
+#
+# 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";;
+(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
+
+cur=1
+
+set -- $(getJankyFrames)
+totalFrames=$1
+jankyFrames=$2
+frameSum=0
+jankSum=0
+
+if [ ${totalFrames:=0} -eq 0 ]; then
+#echo Error: could not read frame info with \"dumpsys graphicsstats\"
+       echo Error: could not read frame info with \"dumpsys gfxinfo\"
+       exit 1
+fi
+
+function swipe {
+       count=0
+       while [ $count -lt $2 ]
+       do
+               doSwipe $1
+               ((count=count+1))
+       done
+}
+
+echo Fling recents...
+doKeyevent APP_SWITCH
+
+while [ $cur -le $iterations ]
+do
+       if [ $capturesystrace -gt 0 ]; then
+               ${ADB}atrace --async_start -z -c -b 16000 freq gfx view idle sched
+       fi
+       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
+
+       set -- $(getJankyFrames)
+       newTotalFrames=$1
+       newJankyFrames=$2
+       ((totalDiff=newTotalFrames-totalFrames))
+       ((frameSum=frameSum+totalDiff))
+       ((jankyDiff=newJankyFrames-jankyFrames))
+       ((jankSum=jankSum+jankyDiff))
+       if [ "$totalDiff" -eq 0 ]; then
+               echo Error: no frames detected. Is the display off?
+               exit 1
+       fi
+       ((jankPct=jankyDiff*100/totalDiff))
+       totalFrames=$newTotalFrames
+       jankyFrames=$newJankyFrames
+
+       echo Frames: $totalDiff Janks: $jankyDiff \(${jankPct}%\)
+       ((cur=cur+1))
+done
+doKeyevent HOME
+((aveJankPct=jankSum*100/frameSum))
+echo AVE: Frames: $frameSum Janks: $jankSum \(${aveJankPct}%\)
diff --git a/tests/workloads/systemapps.sh b/tests/workloads/systemapps.sh
new file mode 100755 (executable)
index 0000000..184c4ee
--- /dev/null
@@ -0,0 +1,212 @@
+# 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
+
+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;;
+       (*)
+               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 "-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
+       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}
+       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"
+}
+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")
+}
+
+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 JankyFrames
+               fi
+       fi
+
+       appnum=-1
+       for app in $appList
+       do
+               vout Starting $app...
+               ((appnum=appnum+1))
+               loopTimestamp=$(date +"%s %N")
+               if [ $totaltimetest -gt 0 ]; then
+                       # no instramentation, just cycle through the apps
+                       if [ $appnum -eq 0 ]; then
+                               printf "%-8s %5s(ms) %3s(ms)\n" App Start Iter
+                       fi
+                       if [ $forcecoldstart -eq 0 ]; then
+                               t=$(startActivity $app)
+                       else
+                               t=$(forceStartActivity $app)
+                       fi
+                       loopEndTimestamp=$(date +"%s %N")
+                       diffTime=$(computeTimeDiff $loopTimestamp $loopEndTimestamp)
+                       # Note: "%d" doesn't work right if run on device
+                       printf "%-10s %5.0f   %5.0f\n" $app $t $diffTime
+                       ((totaltime=totaltime+t))
+                       continue
+               fi
+               tmpTraceOut="$tmpTraceOutBase-$app.out"
+               >$tmpTraceOut
+               startInstramentation
+               resetJankyFrames $(getPackageName $app)
+               t=$(startActivity $app)
+               # let app finish drawing before checking janks
+               sleep 3
+               set -- $(getJankyFrames $(getPackageName $app))
+               frames=$1
+               janks=$2
+               ((jankPct=100*janks/frames))
+               stopAndDumpInstramentation $tmpTraceOut
+               actName=$(getActivityName $app)
+               stime=$(getStartTime $actName $tmpTraceOut)
+               relaunch=$?
+               etime=$(getEndTime $actName $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%%)\n" "$app" "$tdiff" "$t" "$relaunch" "$directReclaim" "$janks" "$jankPct"
+               computeStats "$app" "$tdiff" "$relaunch" "$directReclaim" "$frames" "$janks"
+
+               if [ $savetmpfiles -eq 0 ]; then
+                       rm -f $tmpTraceOut
+               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 JankyFrames
+       for app in $appList
+       do
+               set -- $(getStats $app)
+               sum=$3
+               ((ave=sum/iterations))
+               frames=$6
+               janks=$7
+               ((jankPct=100*janks/frames))
+               printf "%-12s %5d      %5d      %5d      %5d      %5d     %5d(%d%%)\n" $app $1 $ave $2 $4 $5 $janks $jankPct
+       done
+fi