ipynb: add a simple example of rt-app execution on Juno
This provides a first really simple example of usage of devlib and wlgen
to configure and run an rt-app instance on a Juno board.
The trace collected is also visualised using the TRAPpy plotter.
Signed-off-by: Patrick Bellasi <patrick.bellasi@arm.com>
diff --git a/ipynb/simple_rtapp.ipynb b/ipynb/simple_rtapp.ipynb
new file mode 100644
index 0000000..15004e2
--- /dev/null
+++ b/ipynb/simple_rtapp.ipynb
@@ -0,0 +1,723 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "import os\n",
+ "\n",
+ "import devlib\n",
+ "from wlgen import RTA\n",
+ "\n",
+ "import trappy"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Configure logging for this session\n",
+ "import logging\n",
+ "reload(logging)\n",
+ "logging.basicConfig(\n",
+ " format='%(asctime)-9s %(levelname)-8s: %(message)s',\n",
+ " level=logging.DEBUG,\n",
+ " datefmt='%I:%M:%S')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Global configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Host side results folder\n",
+ "RESULTS_DIR = '/tmp/schedtest'\n",
+ "\n",
+ "# Taerget side temporary folder\n",
+ "TARGET_DIR = '/root/schedtest'\n",
+ "\n",
+ "# List of tools to install on the target system\n",
+ "TOOLS = [\"rt-app\", \"trace-cmd\", \"taskset\"]\n",
+ "\n",
+ "# FTrace Configuration\n",
+ "FTRACE_EVENTS = ['sched_switch', 'cpu_frequency']\n",
+ "FTRACE_BUFFZISE = 10240\n",
+ "\n",
+ "# HWMon Configuration\n",
+ "HWMON_CONF = {\n",
+ " 'sites' : [ 'a53', 'a57' ],\n",
+ " 'kinds' : [ 'energy' ]\n",
+ "}\n",
+ "\n",
+ "# List of modules to enable\n",
+ "MODULES = ['bl', 'cpufreq', 'hwmon']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Target configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:18 DEBUG : Installing module bl\n",
+ "01:44:19 DEBUG : Installing module cpufreq\n",
+ "01:44:19 DEBUG : Installing module hwmon\n"
+ ]
+ }
+ ],
+ "source": [
+ "target = devlib.LinuxTarget(\n",
+ " connection_settings={\n",
+ " 'host' : '192.168.0.10',\n",
+ " 'username' : 'root',\n",
+ " 'password' : ''\n",
+ " },\n",
+ " load_default_modules=False,\n",
+ " modules=MODULES,\n",
+ " working_directory=TARGET_DIR\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:47 INFO : Target ABI: arm64, CPus: ['A53', 'A57', 'A57', 'A53', 'A53', 'A53']\n"
+ ]
+ }
+ ],
+ "source": [
+ "logging.info(\"Target ABI: %s, CPus: %s\",\n",
+ " target.abi,\n",
+ " target.cpuinfo.cpu_names)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false,
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:47 INFO : Copy tools required on target\n"
+ ]
+ }
+ ],
+ "source": [
+ "logging.info('Copy tools required on target')\n",
+ "tools_to_install = []\n",
+ "for tool in TOOLS:\n",
+ " # deploy scripts or...\n",
+ " binary = '../tools/scripts/{}'.format(tool)\n",
+ " if not os.path.isfile(binary):\n",
+ " # ... binary tool matching the target ABI\n",
+ " binary = '../tools/{}/{}'.format(target.abi, tool)\n",
+ " tools_to_install.append(binary)\n",
+ "target.setup(tools_to_install)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# FTrace configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:50 INFO : FTrace configured to collect events:\n",
+ "01:44:50 INFO : ['sched_switch', 'cpu_frequency']\n"
+ ]
+ }
+ ],
+ "source": [
+ "ftrace = None\n",
+ "if len(FTRACE_EVENTS):\n",
+ " ftrace = devlib.FtraceCollector(\n",
+ " target,\n",
+ " events = FTRACE_EVENTS,\n",
+ " buffer_size = FTRACE_BUFFZISE,\n",
+ " autoreport = False,\n",
+ " autoview = False\n",
+ " )\n",
+ " logging.info('FTrace configured to collect events:')\n",
+ " logging.info(' %s', FTRACE_EVENTS)\n",
+ "else:\n",
+ " logging.info('FTrace collection disabled by configuration')\n",
+ " \n",
+ "def ftrace_start():\n",
+ " if ftrace is None:\n",
+ " return\n",
+ " ftrace.start()\n",
+ " logging.info('FTrace STARTED')\n",
+ " \n",
+ "def ftrace_stop():\n",
+ " if ftrace is None:\n",
+ " return\n",
+ " ftrace.stop()\n",
+ " logging.info('FTrace STOPPED')\n",
+ " ftrace.get_trace(RESULTS_DIR + '/trace.dat')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Energy Meter configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:51 DEBUG : Discovering available HWMON sensors...\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_amp/curr1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_amp/curr1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_power/power1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_power/power1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_energy/energy1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_energy/energy1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_energy/energy1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_energy/energy1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_amp/curr1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_amp/curr1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_volt/in1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_volt/in1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_volt/in1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_volt/in1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_power/power1\n",
+ "01:44:51 DEBUG : \tAdding sensor v2m_juno_power/power1\n",
+ "01:44:51 INFO : Channels selected for energy sampling:\n",
+ "01:44:51 INFO : [CHAN(v2m_juno_energy/energy1, a53_energy), CHAN(v2m_juno_energy/energy1, a57_energy)]\n"
+ ]
+ }
+ ],
+ "source": [
+ "hwmon = None\n",
+ "if 'hwmon' in MODULES:\n",
+ " hwmon = devlib.HwmonInstrument(target)\n",
+ " hwmon.reset(**HWMON_CONF)\n",
+ " logging.info('Channels selected for energy sampling:')\n",
+ " logging.info(' %s', str(hwmon.active_channels))\n",
+ "else:\n",
+ " logging.info('Energy sampling disabled by configuration')\n",
+ " \n",
+ "# The contained for sampled energy values\n",
+ "energy_reading = {}\n",
+ "\n",
+ "def hwmon_reset():\n",
+ " energy_reading = {}\n",
+ "\n",
+ "# Simple function to compute energy-deltas among consecutive readings\n",
+ "def hwmon_sample():\n",
+ " if hwmon is None:\n",
+ " return\n",
+ " samples = hwmon.take_measurement()\n",
+ " logging.debug('Measure: %s', samples)\n",
+ " for s in samples:\n",
+ " label = s.channel.label\\\n",
+ " .replace('_energy', '')\\\n",
+ " .replace(\" \", \"_\")\n",
+ " value = s.value\n",
+ " logging.debug('Update %s: %s',\n",
+ " label, value)\n",
+ "\n",
+ " if label not in energy_reading:\n",
+ " energy_reading[label] = {\n",
+ " 'last' : value,\n",
+ " 'delta' : 0,\n",
+ " 'total' : 0\n",
+ " }\n",
+ " logging.debug('Initialize %s: %s',\n",
+ " label, energy_reading[label])\n",
+ " continue\n",
+ "\n",
+ " last = energy_reading[label]['last']\n",
+ " delta = value - last\n",
+ " total = energy_reading[label]['total']\n",
+ "\n",
+ " energy_reading[label]['last'] = value\n",
+ " energy_reading[label]['delta'] = delta\n",
+ " energy_reading[label]['total'] = total + delta\n",
+ " \n",
+ " logging.debug('Update %s: %s',\n",
+ " label, energy_reading[label])\n",
+ "\n",
+ " return energy_reading\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Workload configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# RTApp calibration\n",
+ "\n",
+ "# Uncomment the following line to calibrate RTApp the first time\n",
+ "# target_calibration = RTA.calibrate(target)\n",
+ "\n",
+ "# Provide a pre-defined calibration\n",
+ "target_calibration = {0: 353, 1: 138, 2: 138, 3: 353, 4: 354, 5: 360}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:51 ERROR : Assuming taskset is preinstalled\n",
+ "01:44:51 INFO : Setup new workload simple\n",
+ "01:44:51 DEBUG : Setup step [postrun] callback to [__postrun] function\n",
+ "01:44:51 DEBUG : Configuring a profile-based workload...\n",
+ "01:44:51 DEBUG : ref on big cpu: 1\n",
+ "01:44:51 INFO : Workload duration defined by longest task\n",
+ "01:44:51 INFO : ------------------------\n",
+ "01:44:51 INFO : task [task1], SCHED_OTHER:\n",
+ "01:44:51 INFO : | loops count: 1\n",
+ "01:44:51 INFO : + phase_000001: duration 5.000000 [s] (50 loops)\n",
+ "01:44:51 INFO : | period 100000 [us], duty_cycle 20 %\n",
+ "01:44:51 INFO : | run_time 20000 [us], sleep_time 80000 [us]\n"
+ ]
+ }
+ ],
+ "source": [
+ "rtapp = RTA(target, 'simple', calibration=target_calibration)\n",
+ "rtapp.conf(\n",
+ " kind='profile',\n",
+ " params={\n",
+ " 'task1': RTA.periodic(\n",
+ " period_ms=100,\n",
+ " duty_cycle_pct=20,\n",
+ " duration_s=5)\n",
+ " },\n",
+ " run_dir=TARGET_DIR\n",
+ ");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Workload execution"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:51 INFO : Create output folder\n"
+ ]
+ }
+ ],
+ "source": [
+ "logging.info('Create output folder')\n",
+ "os.system('mkdir {}'.format(RESULTS_DIR));"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:44:58 INFO : FTrace STARTED\n",
+ "01:44:59 DEBUG : Measure: [a53_energy: 11588.717062 joules, a57_energy: 16230.437033 joules]\n",
+ "01:44:59 DEBUG : Update a53: 11588.717062\n",
+ "01:44:59 DEBUG : Initialize a53: {'total': 0, 'last': 11588.717062, 'delta': 0}\n",
+ "01:44:59 DEBUG : Update a57: 16230.437033\n",
+ "01:44:59 DEBUG : Initialize a57: {'total': 0, 'last': 16230.437033, 'delta': 0}\n",
+ "01:44:59 INFO : RTApp STARTING...\n",
+ "01:44:59 INFO : Executor [start]: /root/schedtest/bin/rt-app /root/schedtest/simple_00.json\n",
+ "01:45:04 DEBUG : Callback [postrun]...\n",
+ "01:45:04 DEBUG : Pulling logfiles to [/tmp/schedtest]...\n",
+ "01:45:04 DEBUG : Pulling JSON to [/tmp/schedtest]...\n",
+ "01:45:04 DEBUG : Saving output on [/tmp/schedtest/output.log]...\n",
+ "01:45:04 INFO : Executor [end]: /root/schedtest/bin/rt-app /root/schedtest/simple_00.json\n",
+ "01:45:05 DEBUG : Measure: [a53_energy: 11589.953522 joules, a57_energy: 16240.57781 joules]\n",
+ "01:45:05 DEBUG : Update a53: 11589.953522\n",
+ "01:45:05 DEBUG : Update a53: {'total': 1.2364600000000792, 'last': 11589.953522, 'delta': 1.2364600000000792}\n",
+ "01:45:05 DEBUG : Update a57: 16240.57781\n",
+ "01:45:05 DEBUG : Update a57: {'total': 10.140777000000526, 'last': 16240.57781, 'delta': 10.140777000000526}\n",
+ "01:45:06 INFO : FTrace STOPPED\n"
+ ]
+ }
+ ],
+ "source": [
+ "hwmon_reset()\n",
+ "\n",
+ "ftrace_start()\n",
+ "hwmon_sample()\n",
+ "\n",
+ "logging.info('RTApp STARTING...')\n",
+ "rtapp.run(out_dir=RESULTS_DIR)\n",
+ "\n",
+ "nrg = hwmon_sample();\n",
+ "ftrace_stop()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "01:45:09 INFO : Energy: {\n",
+ " \"a53\": {\n",
+ " \"total\": 1.2364600000000792,\n",
+ " \"last\": 11589.953522,\n",
+ " \"delta\": 1.2364600000000792\n",
+ " },\n",
+ " \"a57\": {\n",
+ " \"total\": 10.140777000000526,\n",
+ " \"last\": 16240.57781,\n",
+ " \"delta\": 10.140777000000526\n",
+ " }\n",
+ "}\n"
+ ]
+ }
+ ],
+ "source": [
+ "logging.info('Energy: %s',\n",
+ " json.dumps(nrg,\n",
+ " indent=4,\n",
+ " separators=(',', ': ')))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Trace inspection"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<style>\n",
+ "/*\n",
+ "\n",
+ " * Copyright 2015-2015 ARM Limited\n",
+ "\n",
+ " *\n",
+ "\n",
+ " * Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+ "\n",
+ " * you may not use this file except in compliance with the License.\n",
+ "\n",
+ " * You may obtain a copy of the License at\n",
+ "\n",
+ " *\n",
+ "\n",
+ " * http://www.apache.org/licenses/LICENSE-2.0\n",
+ "\n",
+ " *\n",
+ "\n",
+ " * Unless required by applicable law or agreed to in writing, software\n",
+ "\n",
+ " * distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+ "\n",
+ " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+ "\n",
+ " * See the License for the specific language governing permissions and\n",
+ "\n",
+ " * limitations under the License.\n",
+ "\n",
+ " */\n",
+ "\n",
+ "\n",
+ "\n",
+ ".d3-tip {\n",
+ "\n",
+ " line-height: 1;\n",
+ "\n",
+ " padding: 12px;\n",
+ "\n",
+ " background: rgba(0, 0, 0, 0.6);\n",
+ "\n",
+ " color: #fff;\n",
+ "\n",
+ " border-radius: 2px;\n",
+ "\n",
+ " position: absolute !important;\n",
+ "\n",
+ " z-index: 99999;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".d3-tip:after {\n",
+ "\n",
+ " box-sizing: border-box;\n",
+ "\n",
+ " pointer-events: none;\n",
+ "\n",
+ " display: inline;\n",
+ "\n",
+ " font-size: 10px;\n",
+ "\n",
+ " width: 100%;\n",
+ "\n",
+ " line-height: 1;\n",
+ "\n",
+ " color: rgba(0, 0, 0, 0.6);\n",
+ "\n",
+ " content: \"\\25BC\";\n",
+ "\n",
+ " position: absolute !important;\n",
+ "\n",
+ " z-index: 99999;\n",
+ "\n",
+ " text-align: center;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".d3-tip.n:after {\n",
+ "\n",
+ " margin: -1px 0 0 0;\n",
+ "\n",
+ " top: 100%;\n",
+ "\n",
+ " left: 0;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".contextRect {\n",
+ "\n",
+ " fill: lightgray;\n",
+ "\n",
+ " fill-opacity: 0.5;\n",
+ "\n",
+ " stroke: black;\n",
+ "\n",
+ " stroke-width: 1;\n",
+ "\n",
+ " stroke-opacity: 1;\n",
+ "\n",
+ " pointer-events: none;\n",
+ "\n",
+ " shape-rendering: crispEdges;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".chart {\n",
+ "\n",
+ " shape-rendering: crispEdges;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".mini text {\n",
+ "\n",
+ " font: 9px sans-serif;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".main text {\n",
+ "\n",
+ " font: 12px sans-serif;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".axis line, .axis path {\n",
+ "\n",
+ " stroke: black;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".miniItem {\n",
+ "\n",
+ " stroke-width: 8;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "\n",
+ "\n",
+ ".brush .extent {\n",
+ "\n",
+ "\n",
+ "\n",
+ " stroke: #000;\n",
+ "\n",
+ " fill-opacity: .125;\n",
+ "\n",
+ " shape-rendering: crispEdges;\n",
+ "\n",
+ "}\n",
+ "\n",
+ "</style>\n",
+ "<div id=\"fig_2c57a58c5b13455cad0d583885641163\" class=\"eventplot\">\n",
+ " <script>\n",
+ " var req = require.config( {\n",
+ "\n",
+ " paths: {\n",
+ "\n",
+ " \"EventPlot\": '/nbextensions/plotter_scripts/EventPlot/EventPlot',\n",
+ " \"d3-tip\": '/nbextensions/plotter_scripts/EventPlot/d3.tip.v0.6.3',\n",
+ " \"d3-plotter\": '/nbextensions/plotter_scripts/EventPlot/d3.v3.min'\n",
+ " },\n",
+ " shim: {\n",
+ " \"d3-plotter\" : {\n",
+ " \"exports\" : \"d3\"\n",
+ " },\n",
+ " \"d3-tip\": [\"d3-plotter\"],\n",
+ " \"EventPlot\": {\n",
+ "\n",
+ " \"deps\": [\"d3-tip\", \"d3-plotter\" ],\n",
+ " \"exports\": \"EventPlot\"\n",
+ " }\n",
+ " }\n",
+ " });\n",
+ " req([\"require\", \"EventPlot\"], function() {\n",
+ " EventPlot.generate('fig_2c57a58c5b13455cad0d583885641163', '/nbextensions/');\n",
+ " });\n",
+ " </script>\n",
+ " </div>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "trappy.plotter.plot_trace(RESULTS_DIR)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}