1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2025-02-22 04:49:00 +00:00

Merge pull request #500 from setrofim/next

memcopy workload + fix gem5 support.
This commit is contained in:
setrofim 2017-10-10 08:58:24 +01:00 committed by GitHub
commit c3438b6814
10 changed files with 299 additions and 27 deletions

View File

@ -138,6 +138,9 @@ class ExecutionContext(object):
self.output.write_state() self.output.write_state()
self.output.write_result() self.output.write_result()
def finalize(self):
self.tm.finalize()
def start_job(self): def start_job(self):
if not self.job_queue: if not self.job_queue:
raise RuntimeError('No jobs to run') raise RuntimeError('No jobs to run')
@ -269,7 +272,8 @@ class Executor(object):
self.logger.info('Connecting to target') self.logger.info('Connecting to target')
self.target_manager = TargetManager(config.run_config.device, self.target_manager = TargetManager(config.run_config.device,
config.run_config.device_config) config.run_config.device_config,
output.basepath)
output.write_target_info(self.target_manager.get_target_info()) output.write_target_info(self.target_manager.get_target_info())
self.logger.info('Initializing execution context') self.logger.info('Initializing execution context')
@ -295,6 +299,7 @@ class Executor(object):
runner = Runner(context, pm) runner = Runner(context, pm)
signal.send(signal.RUN_STARTED, self) signal.send(signal.RUN_STARTED, self)
runner.run() runner.run()
context.finalize()
self.execute_postamble(context, output) self.execute_postamble(context, output)
signal.send(signal.RUN_COMPLETED, self) signal.send(signal.RUN_COMPLETED, self)

View File

@ -3,7 +3,8 @@ from copy import copy
from devlib import (LinuxTarget, AndroidTarget, LocalLinuxTarget, from devlib import (LinuxTarget, AndroidTarget, LocalLinuxTarget,
Platform, Juno, TC2, Gem5SimulationPlatform, Platform, Juno, TC2, Gem5SimulationPlatform,
AdbConnection, SshConnection, LocalConnection) AdbConnection, SshConnection, LocalConnection,
Gem5Connection)
from wa.framework import pluginloader from wa.framework import pluginloader
from wa.framework.exception import PluginLoaderError from wa.framework.exception import PluginLoaderError
@ -12,6 +13,7 @@ from wa.framework.target.assistant import LinuxAssistant, AndroidAssistant
from wa.utils.types import list_of_strings, list_of_ints from wa.utils.types import list_of_strings, list_of_ints
from wa.utils.misc import isiterable from wa.utils.misc import isiterable
def get_target_descriptions(loader=pluginloader): def get_target_descriptions(loader=pluginloader):
targets = {} targets = {}
for cls in loader.list_target_descriptors(): for cls in loader.list_target_descriptors():
@ -26,7 +28,7 @@ def get_target_descriptions(loader=pluginloader):
return targets.values() return targets.values()
def instantiate_target(tdesc, params, connect=None): def instantiate_target(tdesc, params, connect=None, extra_platform_params=None):
target_params = {p.name: p for p in tdesc.target_params} target_params = {p.name: p for p in tdesc.target_params}
platform_params = {p.name: p for p in tdesc.platform_params} platform_params = {p.name: p for p in tdesc.platform_params}
conn_params = {p.name: p for p in tdesc.conn_params} conn_params = {p.name: p for p in tdesc.conn_params}
@ -52,6 +54,11 @@ def instantiate_target(tdesc, params, connect=None):
msg = 'Unexpected parameter for {}: {}' msg = 'Unexpected parameter for {}: {}'
raise ValueError(msg.format(tdesc.name, name)) raise ValueError(msg.format(tdesc.name, name))
for pname, pval in (extra_platform_params or {}).iteritems():
if pname in pp:
raise RuntimeError('Platform parameter clash: {}'.format(pname))
pp[pname] = pval
tp['platform'] = (tdesc.platform or Platform)(**pp) tp['platform'] = (tdesc.platform or Platform)(**pp)
if cp: if cp:
tp['connection_settings'] = cp tp['connection_settings'] = cp
@ -230,10 +237,6 @@ VEXPRESS_PLATFORM_PARAMS = [
] ]
GEM5_PLATFORM_PARAMS = [ GEM5_PLATFORM_PARAMS = [
Parameter('host_output_dir', kind=str, mandatory=True,
description='''
Path on the host where gem5 output (e.g. stats file) will be placed.
'''),
Parameter('gem5_bin', kind=str, mandatory=True, Parameter('gem5_bin', kind=str, mandatory=True,
description=''' description='''
Path to the gem5 binary Path to the gem5 binary
@ -247,6 +250,10 @@ GEM5_PLATFORM_PARAMS = [
VirtIO device setup arguments to be passed to gem5. VirtIO is used VirtIO device setup arguments to be passed to gem5. VirtIO is used
to transfer files between the simulation and the host. to transfer files between the simulation and the host.
'''), '''),
Parameter('name', kind=str, default='gem5',
description='''
The name for the gem5 "device".
'''),
] ]
@ -303,6 +310,32 @@ CONNECTION_PARAMS = {
to be run via sudo is to go. to be run via sudo is to go.
"""), """),
], ],
Gem5Connection: [
Parameter('host', kind=str, mandatory=False,
description="""
Host name or IP address of the target.
"""),
Parameter('username', kind=str, default='root',
description="""
User name to connect to gem5 simulation.
"""),
Parameter('password', kind=str,
description="""
Password to use.
"""),
Parameter('port', kind=int,
description="""
The port SSH server is listening on on the target.
"""),
Parameter('password_prompt', kind=str,
description="""
Password prompt to expect
"""),
Parameter('original_prompt', kind=str,
description="""
Original shell prompt to expect.
"""),
],
LocalConnection: [ LocalConnection: [
Parameter('password', kind=str, Parameter('password', kind=str,
description=""" description="""
@ -342,24 +375,26 @@ ASSISTANTS = {
'local': LinuxAssistant, 'local': LinuxAssistant,
} }
# name --> (platform_class, params_list, defaults) # name --> ((platform_class, conn_class), params_list, defaults)
# Note: normally, connection is defined by the Target name, but
# platforms may choose to override it
PLATFORMS = { PLATFORMS = {
'generic': (Platform, COMMON_PLATFORM_PARAMS, None), 'generic': ((Platform, None), COMMON_PLATFORM_PARAMS, None),
'juno': (Juno, COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS, 'juno': ((Juno, None), COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS,
{ {
'vemsd_mount': '/media/JUNO', 'vemsd_mount': '/media/JUNO',
'baudrate': 115200, 'baudrate': 115200,
'bootloader': 'u-boot', 'bootloader': 'u-boot',
'hard_reset_method': 'dtr', 'hard_reset_method': 'dtr',
}), }),
'tc2': (TC2, COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS, 'tc2': ((TC2, None), COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS,
{ {
'vemsd_mount': '/media/VEMSD', 'vemsd_mount': '/media/VEMSD',
'baudrate': 38400, 'baudrate': 38400,
'bootloader': 'bootmon', 'bootloader': 'bootmon',
'hard_reset_method': 'reboottxt', 'hard_reset_method': 'reboottxt',
}), }),
'gem5': (Gem5SimulationPlatform, GEM5_PLATFORM_PARAMS, None), 'gem5': ((Gem5SimulationPlatform, Gem5Connection), GEM5_PLATFORM_PARAMS, None),
} }
@ -382,17 +417,23 @@ class DefaultTargetDescriptor(TargetDescriptor):
assistant = ASSISTANTS[target_name] assistant = ASSISTANTS[target_name]
conn_params = CONNECTION_PARAMS[conn] conn_params = CONNECTION_PARAMS[conn]
for platform_name, platform_tuple in PLATFORMS.iteritems(): for platform_name, platform_tuple in PLATFORMS.iteritems():
platform, platform_params = self._get_item(platform_tuple) (platform, plat_conn), platform_params = self._get_item(platform_tuple)
name = '{}_{}'.format(platform_name, target_name) name = '{}_{}'.format(platform_name, target_name)
td = TargetDescription(name, self) td = TargetDescription(name, self)
td.target = target td.target = target
td.conn = conn
td.platform = platform td.platform = platform
td.assistant = assistant td.assistant = assistant
td.target_params = target_params td.target_params = target_params
td.conn_params = conn_params
td.platform_params = platform_params td.platform_params = platform_params
td.assistant_params = assistant.parameters td.assistant_params = assistant.parameters
if plat_conn:
td.conn = plat_conn
td.conn_params = CONNECTION_PARAMS[plat_conn]
else:
td.conn = conn
td.conn_params = conn_params
result.append(td) result.append(td)
return result return result

View File

@ -1,4 +1,5 @@
import logging import logging
import os
from wa.framework import signal from wa.framework import signal
from wa.framework.plugin import Parameter from wa.framework.plugin import Parameter
@ -8,6 +9,7 @@ from wa.framework.target.descriptor import (get_target_descriptions,
from wa.framework.target.info import TargetInfo from wa.framework.target.info import TargetInfo
from wa.framework.target.runtime_parameter_manager import RuntimeParameterManager from wa.framework.target.runtime_parameter_manager import RuntimeParameterManager
from devlib import Gem5SimulationPlatform
from devlib.utils.misc import memoized from devlib.utils.misc import memoized
from devlib.exception import TargetError from devlib.exception import TargetError
@ -25,7 +27,8 @@ class TargetManager(object):
"""), """),
] ]
def __init__(self, name, parameters): def __init__(self, name, parameters, outdir):
self.outdir = outdir
self.logger = logging.getLogger('tm') self.logger = logging.getLogger('tm')
self.target_name = name self.target_name = name
self.target = None self.target = None
@ -53,8 +56,8 @@ class TargetManager(object):
self.rpm = RuntimeParameterManager(self.target) self.rpm = RuntimeParameterManager(self.target)
def finalize(self): def finalize(self):
self.logger.info('Disconnecting from the device') if self.disconnect or isinstance(self.target.platform, Gem5SimulationPlatform):
if self.disconnect: self.logger.info('Disconnecting from the device')
with signal.wrap('TARGET_DISCONNECT'): with signal.wrap('TARGET_DISCONNECT'):
self.target.disconnect() self.target.disconnect()
@ -85,8 +88,14 @@ class TargetManager(object):
if self.target_name not in target_map: if self.target_name not in target_map:
raise ValueError('Unknown Target: {}'.format(self.target_name)) raise ValueError('Unknown Target: {}'.format(self.target_name))
tdesc = target_map[self.target_name] tdesc = target_map[self.target_name]
extra_plat_params={}
if tdesc.platform is Gem5SimulationPlatform:
extra_plat_params['host_output_dir'] = self.outdir
self.logger.debug('Creating {} target'.format(self.target_name)) self.logger.debug('Creating {} target'.format(self.target_name))
self.target = instantiate_target(tdesc, self.parameters, connect=False) self.target = instantiate_target(tdesc, self.parameters, connect=False,
extra_platform_params=extra_plat_params)
with signal.wrap('TARGET_CONNECT'): with signal.wrap('TARGET_CONNECT'):
self.target.connect() self.target.connect()

View File

@ -40,6 +40,7 @@ from devlib.utils.android import ApkInfo
from wa import Instrument, Parameter, very_fast from wa import Instrument, Parameter, very_fast
from wa.framework import signal from wa.framework import signal
from wa.framework.exception import ConfigError from wa.framework.exception import ConfigError
from wa.framework.instrumentation import slow
from wa.utils.misc import diff_tokens, write_table, check_output, as_relative from wa.utils.misc import diff_tokens, write_table, check_output, as_relative
from wa.utils.misc import ensure_file_directory_exists as _f from wa.utils.misc import ensure_file_directory_exists as _f
from wa.utils.misc import ensure_directory_exists as _d from wa.utils.misc import ensure_directory_exists as _d
@ -127,7 +128,8 @@ class SysfsExtractor(Instrument):
self.target.execute('rm -rf {}'.format(after_dir), as_root=True) self.target.execute('rm -rf {}'.format(after_dir), as_root=True)
self.target.execute('mkdir -p {}'.format(after_dir), as_root=True) self.target.execute('mkdir -p {}'.format(after_dir), as_root=True)
def slow_start(self, context): @slow
def start(self, context):
if self.use_tmpfs: if self.use_tmpfs:
for d in self.paths: for d in self.paths:
dest_dir = self.target.path.join(self.on_device_before, as_relative(d)) dest_dir = self.target.path.join(self.on_device_before, as_relative(d))
@ -139,7 +141,8 @@ class SysfsExtractor(Instrument):
for dev_dir, before_dir, _, _ in self.device_and_host_paths: for dev_dir, before_dir, _, _ in self.device_and_host_paths:
self.target.pull(dev_dir, before_dir) self.target.pull(dev_dir, before_dir)
def slow_stop(self, context): @slow
def stop(self, context):
if self.use_tmpfs: if self.use_tmpfs:
for d in self.paths: for d in self.paths:
dest_dir = self.target.path.join(self.on_device_after, as_relative(d)) dest_dir = self.target.path.join(self.on_device_after, as_relative(d))
@ -192,7 +195,7 @@ class SysfsExtractor(Instrument):
def validate(self): def validate(self):
if not self.tmpfs_mount_point: # pylint: disable=access-member-before-definition if not self.tmpfs_mount_point: # pylint: disable=access-member-before-definition
self.tmpfs_mount_point = self.target.path.join(self.target.working_directory, 'temp-fs') self.tmpfs_mount_point = self.target.get_workpath('temp-fs')
def _local_dir(self, directory): def _local_dir(self, directory):
return os.path.dirname(as_relative(directory).replace(self.target.path.sep, os.sep)) return os.path.dirname(as_relative(directory).replace(self.target.path.sep, os.sep))
@ -256,8 +259,8 @@ class InterruptStatsInstrument(Instrument):
""" """
def __init__(self, device, **kwargs): def __init__(self, target, **kwargs):
super(InterruptStatsInstrument, self).__init__(device, **kwargs) super(InterruptStatsInstrument, self).__init__(target, **kwargs)
self.before_file = None self.before_file = None
self.after_file = None self.after_file = None
self.diff_file = None self.diff_file = None
@ -269,11 +272,11 @@ class InterruptStatsInstrument(Instrument):
def start(self, context): def start(self, context):
with open(_f(self.before_file), 'w') as wfh: with open(_f(self.before_file), 'w') as wfh:
wfh.write(self.device.execute('cat /proc/interrupts')) wfh.write(self.target.execute('cat /proc/interrupts'))
def stop(self, context): def stop(self, context):
with open(_f(self.after_file), 'w') as wfh: with open(_f(self.after_file), 'w') as wfh:
wfh.write(self.device.execute('cat /proc/interrupts')) wfh.write(self.target.execute('cat /proc/interrupts'))
def update_result(self, context): def update_result(self, context):
# If workload execution failed, the after_file may not have been created. # If workload execution failed, the after_file may not have been created.
@ -302,7 +305,7 @@ class DynamicFrequencyInstrument(SysfsExtractor):
super(DynamicFrequencyInstrument, self).setup(context) super(DynamicFrequencyInstrument, self).setup(context)
def validate(self): def validate(self):
# temp-fs would have been set in super's validate, if not explicitly specified. super(DynamicFrequencyInstrument, self).validate()
if not self.tmpfs_mount_point.endswith('-cpufreq'): # pylint: disable=access-member-before-definition if not self.tmpfs_mount_point.endswith('-cpufreq'): # pylint: disable=access-member-before-definition
self.tmpfs_mount_point += '-cpufreq' self.tmpfs_mount_point += '-cpufreq'

View File

@ -74,6 +74,9 @@ def init(verbosity=logging.INFO, color=True, indent_with=4,
if not debug: if not debug:
logging.raiseExceptions = False logging.raiseExceptions = False
logger = logging.getLogger('CGroups')
logger.info = logger.debug
def set_level(level): def set_level(level):
_console_handler.setLevel(level) _console_handler.setLevel(level)

View File

@ -0,0 +1,81 @@
# Copyright 2013-2015 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pylint: disable=E1101,W0201
import os
import re
from wa import Workload, Parameter, Executable
THIS_DIR = os.path.dirname(__file__)
RESULT_REGEX = re.compile('Total time: ([\d.]+) s.*Bandwidth: ([\d.]+) MB/s', re.S)
class Memcpy(Workload):
name = 'memcpy'
description = """
Runs memcpy in a loop.
This will run memcpy in a loop for a specified number of times on a buffer
of a specified size. Additionally, the affinity of the test can be set to
one or more specific cores.
This workload is single-threaded. It genrates no scores or metrics by
itself.
"""
parameters = [
Parameter('buffer_size', kind=int, default=1024 * 1024 * 5,
description='''
Specifies the size, in bytes, of the buffer to be copied.
'''),
Parameter('iterations', kind=int, default=1000,
description='''
Specfies the number of iterations that will be performed.
'''),
Parameter('cpus', kind=list,
description='''
A list of integers specifying ordinals of cores to which the
affinity of the test process should be set. If not specified,
all avaiable cores will be used.
'''),
]
def initialize(self, context):
self.binary_name = 'memcpy'
resource = Executable(self, self.target.abi, self.binary_name)
host_binary = context.resolver.get(resource)
Memcpy.target_exe = self.target.install_if_needed(host_binary)
def setup(self, context):
self.command = '{} -i {} -s {}'.format(self.target_exe, self.iterations, self.buffer_size)
for c in (self.cpus or []):
self.command += ' -c {}'.format(c)
self.result = None
def run(self, context):
self.result = self.target.execute(self.command, timeout=300)
def extract_results(self, context):
if self.result:
match = RESULT_REGEX.search(self.result)
context.add_metric('time', float(match.group(1)), 'seconds', lower_is_better=True)
context.add_metric('bandwidth', float(match.group(2)), 'MB/s')

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
# Copyright 2013-2015 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
${CROSS_COMPILE}gcc -static memcopy.c -o memcopy

View File

@ -0,0 +1,114 @@
/* Copyright 2013-2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <time.h>
const int MAX_CPUS = 8;
const int DEFAULT_ITERATIONS = 1000;
const int DEFAULT_BUFFER_SIZE = 1024 * 1024 * 5;
int set_affinity(size_t cpus_size, int* cpus)
{
int i;
int mask = 0;
for(i = 0; i < cpus_size; ++i)
{
mask |= 1 << cpus[i];
}
return syscall(__NR_sched_setaffinity, 0, sizeof(mask), &mask);
}
int main(int argc, char** argv)
{
int cpus[MAX_CPUS];
int next_cpu = 0;
int iterations = DEFAULT_ITERATIONS;
int buffer_size = DEFAULT_BUFFER_SIZE;
int c;
while ((c = getopt(argc, argv, "i:c:s:")) != -1)
switch (c)
{
case 'c':
cpus[next_cpu++] = atoi(optarg);
if (next_cpu == MAX_CPUS)
{
fprintf(stderr, "Max CPUs exceeded.");
abort();
}
break;
case 'i':
iterations = atoi(optarg);
break;
case 's':
buffer_size = atoi(optarg);
break;
default:
abort();
break;
}
int ret;
if (next_cpu != 0)
if (ret = set_affinity(next_cpu, cpus))
{
fprintf(stderr, "sched_setaffinity returnred %i.", ret);
abort();
}
char* source = malloc(buffer_size);
char* dest = malloc(buffer_size);
struct timespec before, after;
if (clock_gettime(CLOCK_MONOTONIC, &before))
{
fprintf(stderr, "Could not get start time.");
abort();
}
int i;
for (i = 0; i < iterations; ++i)
{
memcpy(dest, source, buffer_size);
}
if (clock_gettime(CLOCK_MONOTONIC, &after))
{
fprintf(stderr, "Could not get end time.");
abort();
}
free(dest);
free(source);
long delta_sec = (long)(after.tv_sec - before.tv_sec);
long delta_nsec = after.tv_nsec - before.tv_nsec;
double delta = (double)delta_sec + delta_nsec / 1e9;
printf("Total time: %f s\n", delta);
printf("Bandwidth: %f MB/s\n", buffer_size / delta * iterations / 1e6);
return 0;
}