=== added file 'doc/arm_energy_probe.rst'
@@ -0,0 +1,102 @@
+Using the ARM Energy Probe
+==========================
+
+The dispatcher includes a `signal handler`_ that allows tests in LAVA
+to include power measurement data per test case. Since the functionality
+is built into the dispatcher there are really two main things required to
+enable this.
+
+ * deployment of a device with the AEP
+ * creating jobs to use it
+
+.. _`signal handler`: external_measurement.html
+
+Deployment
+----------
+
+Hooking up probes to a specific board are beyond the scope of this document.
+However, once a board has a probe hooked up and plugged into the host PC,
+the dispatcher can be configured as follows::
+
+ # These options should be added to the device.conf ie:
+ # /srv/lava/instances/<INST>/etc/lava-dispatcher/devices/panda01.conf
+ # if the defaults are what's needed, then this can be skipped
+
+ # The location of the binary (default=/usr/local/bin/arm-probe)
+ arm_probe_binary = /home/doanac/linaro/arm-probe/arm-probe/arm-probe
+
+ # The location of the config file (default=/usr/local/etc/arm-probe-config)
+ arm_probe_config = /home/doanac/linaro/arm-probe/config
+
+ # The channels configured for this probe (can be an array default=VDD_VCORE1)
+ arm_probe_channels = VDD_VCORE1
+
+Since there may be a mix of device that have AEPs and different configs for
+the ones that do, its also recommended to use the LAVA admin interface for
+the lava-scheduler to define some tagging scheme that can be used to identify
+devices with certain AEP configs. This allows job files to then specify a
+tag if it needs AEP or some special AEP config.
+
+Creating a Job File
+-------------------
+
+The job is pretty standard and can be read about our `jobfile`_ documentation.
+The specific thing needed for an AEP job would be the lava-test-shell action
+which would look something like::
+
+ {
+ "command": "lava_test_shell",
+ "parameters": {
+ "testdef_repos": [
+ {"bzr-repo": "lp:~doanac/+junk/arm-probe-demo",
+ "testdef": "arm-probe.yaml"
+ }
+ ],
+ "timeout": 1800
+ }
+ }
+
+.. _`jobfile`: jobfile.html
+
+Specifying the Test Definition
+------------------------------
+
+The test definintion should live in a bzr/git repository. The `above example's`_
+test definintion would look like::
+
+ metadata:
+ format: Lava-Test Test Definition 1.0
+ name: arm-probe-demo
+
+ handler:
+ handler-name: arm-probe
+ params:
+ # The post_process_script is run for each test case. Its called like:
+ # <script> <testcase_id> <aep stdout> <aep stderr> <aep channel_1>...
+ # This value can be either a relative path in the repo it lives in, or
+ # can be URL that will be downloaded
+ post_process_script: plot.sh
+ # probe_args allow you to add additional parameters when invoking the
+ # arm-probe binary
+ probe_args:
+ - -a
+ - '0.01'
+
+ install:
+ deps:
+ - cpuburn
+
+ run:
+ steps:
+ # These steps run on the target. lava-test-shell will call your script
+ # and ensure the host starts/stops the arm-probe for each test case
+ - 'lava-test-case aep-idle --shell ./aep-idle.sh'
+ - 'lava-test-case aep-burn --shell ./aep-burn.sh'
+
+
+Upon completion of the test run, the dispatcher will invoke the provided
+`postprocess_test_result`_ script so that it can generate things like graphs as it sees
+fit to compliment the data normally captured by LAVA.
+
+.. _`above example's`: http://bazaar.launchpad.net/~doanac/+junk/arm-probe-demo/files
+.. _`postprocess_test_result`: http://bazaar.launchpad.net/~doanac/+junk/arm-probe-demo/view/head:/plot.sh
=== modified file 'doc/index.rst'
@@ -35,6 +35,7 @@
usage.rst
lava_test_shell.rst
external_measurement.rst
+ arm_energy_probe.rst
proxy.rst
* :ref:`search`
=== modified file 'lava_dispatcher/config.py'
@@ -82,6 +82,10 @@
android_orig_block_device = schema.StringOption(default="mmcblk0")
android_lava_block_device = schema.StringOption(default="mmcblk0")
+ arm_probe_binary = schema.StringOption(default='/usr/local/bin/arm-probe')
+ arm_probe_config = schema.StringOption(default='/usr/local/etc/arm-probe-config')
+ arm_probe_channels = schema.ListOption(default=['VDD_VCORE1'])
+
class OptionDescriptor(object):
def __init__(self, name):
=== modified file 'lava_dispatcher/signals/__init__.py'
@@ -18,7 +18,16 @@
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
+import contextlib
import logging
+import tempfile
+
+from lava_dispatcher.utils import rmtree
+
+from lava_dispatcher.lava_test_shell import (
+ _result_to_dir,
+ _result_from_dir,
+)
class BaseSignalHandler(object):
@@ -91,6 +100,18 @@
except:
logging.exception("postprocess_test_result failed for %s", tc_id)
+ @contextlib.contextmanager
+ def _result_as_dir(self, test_result):
+ scratch_dir = self.testdef_obj.context.client.target_device.scratch_dir
+ rdir = tempfile.mkdtemp(dir=scratch_dir)
+ try:
+ _result_to_dir(test_result, rdir)
+ yield rdir
+ test_result.clear()
+ test_result.update(_result_from_dir(rdir))
+ finally:
+ rmtree(rdir)
+
def start_testcase(self, test_case_id):
return {}
=== added file 'lava_dispatcher/signals/armprobe.py'
@@ -0,0 +1,77 @@
+import logging
+import os
+import subprocess
+import urlparse
+
+from lava_dispatcher.downloader import download_image
+from lava_dispatcher.signals import SignalHandler
+
+
+class ArmProbe(SignalHandler):
+
+ def __init__(self, testdef_obj, post_process_script, probe_args=None):
+ SignalHandler.__init__(self, testdef_obj)
+
+ self.scratch_dir = testdef_obj.context.client.target_device.scratch_dir
+
+ # post_process_script can be local to the repo or a URL
+ if not urlparse.urlparse(post_process_script).scheme:
+ self.post_process_script = os.path.join(
+ testdef_obj.repo, post_process_script)
+ else:
+ self.post_process_script = download_image(
+ post_process_script, testdef_obj.context, self.scratch_dir)
+ os.chmod(self.post_process_script, 755) # make sure we can execute it
+
+ # build up the command we'll use for running the probe
+ config = testdef_obj.context.client.config
+ self.aep_channels = config.arm_probe_channels
+ self.aep_args = [
+ config.arm_probe_binary, '-C', config.arm_probe_config]
+ for c in self.aep_channels:
+ self.aep_args.append('-c')
+ self.aep_args.append(c)
+
+ for arg in probe_args:
+ self.aep_args.append(arg)
+
+ def start_testcase(self, test_case_id):
+ ofile = os.path.join(self.scratch_dir, '%s.out' % test_case_id)
+ efile = os.path.join(self.scratch_dir, '%s.err' % test_case_id)
+ ofile = open(ofile, 'w')
+ efile = open(efile, 'w')
+
+ proc = subprocess.Popen(
+ self.aep_args, stdout=ofile, stderr=efile, stdin=subprocess.PIPE)
+ # The arm-probe-binary allows you to write to stdin via a pipe and
+ # includes the content as comments in the header of its output
+ proc.stdin.write(
+ '# run from lava-test-shell with args: %r' % self.aep_args)
+ proc.stdin.close()
+
+ return {
+ 'process': proc,
+ 'logfile': ofile,
+ 'errfile': efile,
+ }
+
+ def end_testcase(self, test_case_id, data):
+ proc = data['process']
+ proc.terminate()
+
+ def postprocess_test_result(self, test_result, data):
+ tcid = test_result['test_case_id']
+ logging.info('analyzing aep data for %s ...', tcid)
+
+ lfile = data['logfile']
+ efile = data['errfile']
+
+ lfile.close()
+ efile.close()
+
+ with self._result_as_dir(test_result) as result_dir:
+ args = [self.post_process_script, tcid, lfile.name, efile.name]
+ args.extend(self.aep_channels)
+
+ if subprocess.call(args, cwd=result_dir) != 0:
+ logging.warn('error calling post_process_script')
=== modified file 'lava_dispatcher/signals/shellhooks.py'
@@ -5,13 +5,11 @@
import os
import tempfile
-from lava_dispatcher.lava_test_shell import (
- _read_content,
- _result_to_dir,
- _result_from_dir)
+from lava_dispatcher.lava_test_shell import _read_content
from lava_dispatcher.signals import SignalHandler
from lava_dispatcher.test_data import create_attachment
-from lava_dispatcher.utils import mkdtemp, rmtree
+from lava_dispatcher.utils import mkdtemp
+
class ShellHooks(SignalHandler):
@@ -64,24 +62,16 @@
'end_testcase', case_data['case_dir'])
def postprocess_test_result(self, test_result, case_data):
- test_case_id = test_result['test_case_id']
- scratch_dir = tempfile.mkdtemp()
- try:
- result_dir = os.path.join(scratch_dir, test_case_id)
- os.mkdir(result_dir)
- _result_to_dir(test_result, result_dir)
+ with self._result_as_dir(test_result) as result_dir:
case_data['postprocess_test_result_output'] = self._invoke_hook(
'postprocess_test_result', case_data['case_dir'], [result_dir])
- test_result.clear()
- test_result.update(_result_from_dir(result_dir))
- finally:
- rmtree(scratch_dir)
+
for key in 'start_testcase_output', 'end_testcase_output', \
- 'postprocess_test_result_output':
- path = case_data.get(key)
- if path is None:
- continue
- content = _read_content(path, ignore_missing=True)
- if content:
- test_result['attachments'].append(
- create_attachment(key + '.txt', _read_content(path)))
+ 'postprocess_test_result_output':
+ path = case_data.get(key)
+ if path is None:
+ continue
+ content = _read_content(path, ignore_missing=True)
+ if content:
+ test_result['attachments'].append(
+ create_attachment(key + '.txt', _read_content(path)))
=== modified file 'setup.py'
@@ -20,6 +20,7 @@
[lava.signal_handlers]
add-duration = lava_dispatcher.signals.duration:AddDuration
+ arm-probe = lava_dispatcher.signals.armprobe:ArmProbe
shell-hooks = lava_dispatcher.signals.shellhooks:ShellHooks
""",
packages=find_packages(),