This tutorial aims to show how to configure a test environment using the TestEnv module provided by LISA.
In [1]:
import logging
from conf import LisaLogging
LisaLogging.setup()
In [2]:
# Execute this cell to enabled devlib debugging statements
logging.getLogger('ssh').setLevel(logging.DEBUG)
In [3]:
# Other python modules required by this notebook
import json
import time
import os
In [4]:
# Custom scrips must be deployed under $LISA_HOME/tools
!tree ../../tools
In [5]:
# This is the (not so fancy) script we want to deploy
!cat ../../tools/scripts/cpuidle_sampling.sh
In [6]:
# You can have a look at the devlib supported modules by lising the
devlib_modules_folder = 'libs/devlib/devlib/module/'
logging.info("Devlib provided modules are found under:")
logging.info(" $LISA_HOME/{}".format(devlib_modules_folder))
!cd ../../ ; find {devlib_modules_folder} -name "*.py" | sed 's|libs/devlib/devlib/module/| |' | grep -v __init__
In [7]:
# Setup a target configuration
conf = {
# Define the kind of target platform to use for the experiments
"platform" : 'linux', # platform type, valid other options are:
# android - access via ADB
# linux - access via SSH
# host - direct access
# Preload settings for a specific target
"board" : 'juno', # board type, valid options are:
# - juno - JUNO Development Board
# - tc2 - TC2 Development Board
# Login credentials
"host" : "192.168.0.1",
"username" : "root",
"password" : "",
# Custom tools to deploy on target, they must be placed under:
# $LISA_HOME/tools/(ARCH|scripts)
"tools" : [ "cpuidle_sampling.sh" ],
# FTrace configuration
"ftrace" : {
"events" : [
"cpu_idle",
"sched_switch",
],
"buffsize" : 10240,
},
# Where results are collected
"results_dir" : "TestEnvExample",
# Devlib module required (or not required)
'modules' : [ "cpufreq", "cgroups" ],
#"exclude_modules" : [ "hwmon" ],
# Local installation path used for kernel/dtb installation on target
# The specified path MUST be accessible from the board, e.g.
# - JUNO/TC2: it can be the mount path of the VMESD disk image
# - Other board: it can be a TFTP server path used by the board bootloader
"tftp" : {
"folder" : "/var/lib/tftpboot",
"kernel" : "kern.bin",
"dtb" : "dtb.bin",
},
}
In [8]:
from env import TestEnv
# Initialize a test environment using the provided configuration
te = TestEnv(conf)
The initialization of the test environment pre-initialize some useful
environment variables which are available to write test cases.
These are some of the information available via the TestEnv object.
In [9]:
# The complete configuration of the target we have configured
print json.dumps(te.conf, indent=4)
In [10]:
# Last configured kernel and DTB image
print te.kernel
print te.dtb
In [11]:
# The IP and MAC address of the target
print te.ip
print te.mac
In [12]:
# A full platform descriptor
print json.dumps(te.platform, indent=4)
In [13]:
# A pre-created folder to host the tests results generated using this
# test environment, notice that the suite could add additional information
# in this folder, like for example a copy of the target configuration
# and other target specific collected information
te.res_dir
Out[13]:
In [14]:
# The working directory on the target
te.workdir
Out[14]:
Some methods are also exposed to test developers which could be used to easy the creation of tests.
These are some of the methods available:
In [15]:
# Calibrate RT-App (if required) and get the most updated calibration value
te.calibration()
In [16]:
# Generate a JSON file with the complete platform description
te.platform_dump(dest_dir='/tmp')
Out[16]:
In [17]:
# Force a reboot of the target (and wait specified [s] before reconnect)
# te.reboot(reboot_time=60, ping_time=15)
In [18]:
# Resolve a MAC address into an IP address
# te.resolv_host(host='00:02:F7:00:5A:5B')
In [19]:
# Copy the specified file into the TFTP server folder defined by configuration
te.tftp_deploy('/etc/group')
In [20]:
!ls -la /var/lib/tftpboot
A special TestEnv attribute is target, which represents a devlib instance. Using the target attribute we can access to the full set of devlib provided functionalities. Which are summarized in the following sections.
In [22]:
# Run a command on the target
te.target.execute("echo -n 'Hello Test Environment'", as_root=False)
Out[22]:
In [23]:
# Spawn a command in background on the target
logging.info("Spawn a task which will run for a while...")
process = te.target.kick_off("sleep 10", as_root=True)
In [24]:
output = te.target.execute("ps")
print '\n'.join(output.splitlines())
Notice that the Shell PID is always the same for all commands we execute.
This is due to devlib ensuring to keep a persistent connection with the target device.
In [27]:
my_script = te.target.get_installed("cpuidle_sampling.sh")
print my_script
In [28]:
output = te.target.execute(my_script, as_root=True)
output.splitlines()
Out[28]:
Notice that the output is returned as a list of lines. This provides a useful base for post-processing the output of that command.
In [35]:
# We can also use "notebook embedded" scripts
# my_script = " \
# for I in $(seq 3); do \
# grep '' /sys/devices/system/cpu/cpu*/cpufreq/stats/time_in_stats | \
# sed -e 's|/sys/devices/system/cpu/cpu||' -e 's|/cpufreq/scaling_governor:| |' \
# sleep 1 \
# done \
# "
In [34]:
# print te.target.execute(my_script)
In [9]:
# Acces to many target specific information
print "ABI : ", te.target.abi
print "big Core Family : ", te.target.big_core
print "LITTLE Core Family : ", te.target.little_core
print "CPU's Clusters IDs : ", te.target.core_clusters
print "CPUs type : ", te.target.core_names
In [10]:
# Access to big.LITTLE specific information
print "big CPUs IDs : ", te.target.bl.bigs
print "LITTLE CPUs IDs : ", te.target.bl.littles
print "big CPUs freqs : {}".format(te.target.bl.get_bigs_frequency())
print "big CPUs governor : {}".format(te.target.bl.get_bigs_governor())
In [11]:
# You can use autocompletion to have a look at the supported method for a
# specific module
te.target.cpufreq #.get_all_governors()
Out[11]:
In [16]:
# Get goverors available for CPU0
te.target.cpufreq.list_governors(0)
Out[16]:
In [17]:
# Set the "ondemand" governor
te.target.cpufreq.set_governor(0, 'ondemand')
In [19]:
# Check governor tunables
te.target.cpufreq.get_governor_tunables(0)
Out[19]:
In [22]:
# Update governor tunables
te.target.cpufreq.set_governor_tunables(0, sampling_rate=2000000)
te.target.cpufreq.get_governor_tunables(0)
Out[22]:
In [44]:
logging.info('%14s - Available controllers:', 'CGroup')
ssys = target.cgroups.list_subsystems()
for (n,h,g,e) in ssys:
print '{:10} (hierarchy id: {:d}) has {} cgroups'.format(n, h, g)
In [23]:
# Get a reference to the CPUSet controller
cpuset = target.cgroups.controller('cpuset')
In [47]:
# Get the list of current configured CGroups for that controller
cgroups = cpuset.list_all()
print 'Existing CGropups:'
for cg in cgroups:
print " ", cg
In [29]:
# Create a LITTLE partition and check which tunables we have
cpuset_littles = cpuset.cgroup('/LITTLE')
cpuset_littles.get()
Out[29]:
In [33]:
# Setup CPUs and MEMORY nodes for the LITTLE partition
cpuset_littles.set(cpus=te.target.bl.littles, mems=0)
In [52]:
# Dump the configuraiton of each controller
for cgname in cgroups:
cgroup = cpuset.cgroup(cgname)
attrs = cgroup.get()
cpus = attrs['cpus']
print '{}:{:<15} cpus: {}'.format(cpuset.kind, cgroup.name, cpus)
In [53]:
# Methods exists to move tasks in/out and in between groups
# cpuset_littles.add_task()
In [6]:
# Reset and sample energy counters
te.emeter.reset()
# Sleep some time
time.sleep(2)
# Sample energy consumption since last reset
nrg = te.emeter.sample()
nrg = json.dumps(te.emeter.sample(), indent=4)
print "First read: ", nrg
# Sleep some more time
time.sleep(2)
# Sample again
nrg = te.emeter.sample()
nrg = json.dumps(te.emeter.sample(), indent=4)
print "Second read: ", nrg
In [7]:
# Configure a specific set of events to trace
te.ftrace_conf(
{
"events" : [
"cpu_idle",
"cpu_capacity",
"cpu_frequency",
"sched_switch",
],
"buffsize" : 10240
}
)
In [8]:
# Start/Stop a FTrace session
te.ftrace.start()
te.target.execute("uname -a")
te.ftrace.stop()
In [9]:
# Collect and visualize the trace
trace_file = os.path.join(te.res_dir, 'trace.dat')
te.ftrace.get_trace(trace_file)
output = os.popen("DISPLAY=:0.0 kernelshark {}".format(trace_file))