The goal of this experiment is to collect frame statistics while swiping up and down tabs of recently opened applications on a Pixel device running Android with an EAS kernel. This process is name Recents Fling. The Analysis phase will consist in comparing EAS with other schedulers, that is comparing sched governor with:
- interactive
- performance
- powersave
- ondemand
For this experiment it is recommended to open many applications so that we can swipe over more recently opened applications.
In [1]:
import logging
from conf import LisaLogging
LisaLogging.setup()
In [2]:
%pylab inline
import os
from time import sleep
# Support to access the remote target
import devlib
from env import TestEnv
# Import support for Android devices
from android import Screen, Workload
from devlib.utils.android import adb_command
# Support for trace events analysis
from trace import Trace
# Suport for FTrace events parsing and visualization
import trappy
devlib requires the ANDROID_HOME environment variable configured to point to your local installation of the Android SDK. If you have not this variable configured in the shell used to start the notebook server, you need to run a cell to define where your Android SDK is installed or specify the ANDROID_HOME in your target configuration.
In case more than one Android device are conencted to the host, you must specify the ID of the device you want to target in my_target_conf. Run adb devices on your host to get the ID.
In [3]:
import os
os.environ['ANDROID_HOME'] = '/ext/android-sdk-linux/'
In case more than one Android device are conencted to the host, you must specify the ID of the device you want to target in my_target_conf
. Run adb devices
on your host to get the ID.
In [4]:
# Setup a target configuration
my_conf = {
# Target platform and board
"platform" : 'android',
"board" : 'pixel',
# Device ID
"device" : "HT6670300102",
# Android home
"ANDROID_HOME" : "/home/vagrant/lisa/tools/android-sdk-linux",
# Folder where all the results will be collected
"results_dir" : "Android_RecentsFling",
# Define devlib modules to load
"modules" : [
'cpufreq' # enable CPUFreq support
],
# FTrace events to collect for all the tests configuration which have
# the "ftrace" flag enabled
"ftrace" : {
"events" : [
"sched_switch",
"sched_load_avg_cpu",
"cpu_frequency",
"cpu_capacity"
],
"buffsize" : 10 * 1024,
},
# Tools required by the experiments
"tools" : [ 'trace-cmd' ],
}
In [5]:
# Initialize a test environment using:
te = TestEnv(my_conf)
target = te.target
This set of support functions will help us running the benchmark using different CPUFreq governors.
In [6]:
def set_performance():
target.cpufreq.set_all_governors('performance')
def set_powersave():
target.cpufreq.set_all_governors('powersave')
def set_interactive():
target.cpufreq.set_all_governors('interactive')
def set_sched():
target.cpufreq.set_all_governors('sched')
def set_ondemand():
target.cpufreq.set_all_governors('ondemand')
for cpu in target.list_online_cpus():
tunables = target.cpufreq.get_governor_tunables(cpu)
target.cpufreq.set_governor_tunables(
cpu,
'ondemand',
**{'sampling_rate' : tunables['sampling_rate_min']}
)
In [7]:
# CPUFreq configurations to test
confs = {
'performance' : {
'label' : 'prf',
'set' : set_performance,
},
'powersave' : {
'label' : 'pws',
'set' : set_powersave,
},
'interactive' : {
'label' : 'int',
'set' : set_interactive,
},
'sched' : {
'label' : 'sch',
'set' : set_sched,
},
'ondemand' : {
'label' : 'odm',
'set' : set_ondemand,
}
}
# The set of results for each comparison test
results = {}
In [8]:
def open_apps(n):
"""
Open `n` apps on the device
:param n: number of apps to open
:type n: int
"""
# Get a list of third-party packages
android_version = target.getprop('ro.build.version.release')
if android_version >= 'N':
packages = target.execute('cmd package list packages | cut -d: -f 2')
packages = packages.splitlines()
else:
packages = target.execute('pm list packages -3 | cut -d: -f 2')
packages = packages.splitlines()
# As a safe fallback let's use a list of standard Android AOSP apps which are always available
if len(packages) < 8:
packages = [
'com.android.messaging',
'com.android.calendar',
'com.android.settings',
'com.android.calculator2',
'com.android.email',
'com.android.music',
'com.android.deskclock',
'com.android.contacts',
]
LAUNCH_CMD = 'monkey -p {} -c android.intent.category.LAUNCHER 1 '
if n > len(packages):
n = len(packages)
logging.info('Trying to open %d apps...', n)
started = 0
for app in packages:
logging.debug(' Launching %s', app)
try:
target.execute(LAUNCH_CMD.format(app))
started = started + 1
logging.info(' %2d starting %s...', started, app)
except Exception:
pass
if started >= n:
break
# Close Recents
target.execute('input keyevent KEYCODE_HOME')
In [9]:
def recentsfling_run(exp_dir):
# Unlock device screen (assume no password required)
target.execute('input keyevent 82')
# Configure screen to max brightness and no dimming
Screen.set_brightness(target, percent=100)
Screen.set_dim(target, auto=False)
Screen.set_timeout(target, 60*60*10) # 10 hours should be enought for an experiment
# Open Recents on the target device
target.execute('input keyevent KEYCODE_APP_SWITCH')
# Allow the activity to start
sleep(5)
# Reset framestats collection
target.execute('dumpsys gfxinfo --reset')
w, h = target.screen_resolution
x = w/2
yl = int(0.2*h)
yh = int(0.9*h)
logging.info('Start Swiping Recents')
for i in range(5):
# Simulate two fast UP and DOWN swipes
target.execute('input swipe {} {} {} {} 50'.format(x, yl, x, yh))
sleep(0.3)
target.execute('input swipe {} {} {} {} 50'.format(x, yh, x, yl))
sleep(0.7)
logging.info('Swiping Recents Completed')
# Reset screen brightness and auto dimming
Screen.set_defaults(target)
# Get frame stats
framestats_file = os.path.join(exp_dir, "framestats.txt")
adb_command(target.adb_name, 'shell dumpsys gfxinfo com.android.systemui > {}'.format(framestats_file))
# Close Recents
target.execute('input keyevent KEYCODE_HOME')
return framestats_file
In [10]:
def experiment(governor, exp_dir):
os.system('mkdir -p {}'.format(exp_dir));
logging.info('------------------------')
logging.info('Run workload using %s governor', governor)
confs[governor]['set']()
# Start FTrace
te.ftrace.start()
### Run the benchmark ###
framestats_file = recentsfling_run(exp_dir)
# Stop FTrace
te.ftrace.stop()
# Collect and keep track of the trace
trace_file = os.path.join(exp_dir, 'trace.dat')
te.ftrace.get_trace(trace_file)
# Parse trace
tr = Trace(exp_dir,
my_conf['ftrace']['events'],
te.platform)
# return all the experiment data
return {
'dir' : exp_dir,
'framestats_file' : framestats_file,
'trace_file' : trace_file,
'ftrace' : tr.ftrace,
'trace' : tr
}
In [11]:
N_APPS = 20
In [12]:
open_apps(N_APPS)
# Give apps enough time to open
sleep(5)
In [13]:
# Unlock device screen (assume no password required)
target.execute('input keyevent 82')
# Run the benchmark in all the configured governors
for governor in confs:
test_dir = os.path.join(te.res_dir, governor)
results[governor] = experiment(governor, test_dir)
In [14]:
for governor in confs:
framestats_file = results[governor]['framestats_file']
print "Frame Statistics for {} governor".format(governor.upper())
!sed '/Stats since/,/99th/!d;/99th/q' $framestats_file
print ""