mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-21 20:38:57 +00:00
Merge pull request #447 from marcbonnici/energy_measurement
Energy measurement
This commit is contained in:
commit
531dcfbb1b
@ -186,7 +186,7 @@ def pop_aliased_param(cfg_point, d, default=None):
|
|||||||
aliases = [cfg_point.name] + cfg_point.aliases
|
aliases = [cfg_point.name] + cfg_point.aliases
|
||||||
alias_map = [a for a in aliases if a in d]
|
alias_map = [a for a in aliases if a in d]
|
||||||
if len(alias_map) > 1:
|
if len(alias_map) > 1:
|
||||||
raise ConfigError(DUPLICATE_ENTRY_ERROR.format(aliases))
|
raise ConfigError('Duplicate entry: {}'.format(aliases))
|
||||||
elif alias_map:
|
elif alias_map:
|
||||||
return d.pop(alias_map[0])
|
return d.pop(alias_map[0])
|
||||||
else:
|
else:
|
||||||
|
249
wa/instrumentation/energy_measurement.py
Normal file
249
wa/instrumentation/energy_measurement.py
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
# Copyright 2013-2017 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=W0613,E1101
|
||||||
|
from __future__ import division
|
||||||
|
import os
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from devlib.instrument import CONTINUOUS
|
||||||
|
from devlib.instrument.energy_probe import EnergyProbeInstrument
|
||||||
|
from devlib.instrument.daq import DaqInstrument
|
||||||
|
|
||||||
|
from wa import Instrument, Parameter
|
||||||
|
from wa.framework import pluginloader
|
||||||
|
from wa.framework.plugin import Plugin
|
||||||
|
from wa.framework.exception import ConfigError
|
||||||
|
from wa.utils.types import list_of_strings, list_of_ints, list_or_string
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyInstrumentBackend(Plugin):
|
||||||
|
|
||||||
|
name = None
|
||||||
|
kind = 'energy_instrument_backend'
|
||||||
|
parameters = []
|
||||||
|
|
||||||
|
instrument = None
|
||||||
|
|
||||||
|
def get_parameters(self):
|
||||||
|
return {p.name : p for p in self.parameters}
|
||||||
|
|
||||||
|
def validate_parameters(self, params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DAQBackend(EnergyInstrumentBackend):
|
||||||
|
|
||||||
|
name = 'daq'
|
||||||
|
|
||||||
|
parameters = [
|
||||||
|
Parameter('resistor_values', kind=list_of_ints,
|
||||||
|
description="""
|
||||||
|
The values of resistors (in Ohms) across which the voltages
|
||||||
|
are measured on.
|
||||||
|
"""),
|
||||||
|
Parameter('labels', kind=list_of_strings,
|
||||||
|
description="""
|
||||||
|
'List of port labels. If specified, the length of the list
|
||||||
|
must match the length of ``resistor_values``.
|
||||||
|
"""),
|
||||||
|
Parameter('host', kind=str, default='localhost',
|
||||||
|
description="""
|
||||||
|
The host address of the machine that runs the daq Server which
|
||||||
|
the instrument communicates with.
|
||||||
|
"""),
|
||||||
|
Parameter('port', kind=int, default=45677,
|
||||||
|
description="""
|
||||||
|
The port number for daq Server in which daq instrument
|
||||||
|
communicates with.
|
||||||
|
"""),
|
||||||
|
Parameter('device_id', kind=str, default='Dev1',
|
||||||
|
description="""
|
||||||
|
The ID under which the DAQ is registered with the driver.
|
||||||
|
"""),
|
||||||
|
Parameter('v_range', kind=str, default=2.5,
|
||||||
|
description="""
|
||||||
|
Specifies the voltage range for the SOC voltage channel on the
|
||||||
|
DAQ (please refer to :ref:`daq_setup` for details).
|
||||||
|
"""),
|
||||||
|
Parameter('dv_range', kind=str, default=0.2,
|
||||||
|
description="""
|
||||||
|
Specifies the voltage range for the resistor voltage channel
|
||||||
|
on the DAQ (please refer to :ref:`daq_setup` for details).
|
||||||
|
"""),
|
||||||
|
Parameter('sample_rate_hz', kind=str, default=10000,
|
||||||
|
description="""
|
||||||
|
Specify the sample rate in Hz.
|
||||||
|
"""),
|
||||||
|
Parameter('channel_map', kind=list_of_ints,
|
||||||
|
default=(0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23),
|
||||||
|
description="""
|
||||||
|
Represents mapping from logical AI channel number to physical
|
||||||
|
connector on the DAQ (varies between DAQ models). The default
|
||||||
|
assumes DAQ 6363 and similar with AI channels on connectors
|
||||||
|
0-7 and 16-23.
|
||||||
|
""")
|
||||||
|
]
|
||||||
|
|
||||||
|
instrument = DaqInstrument
|
||||||
|
|
||||||
|
def validate_parameters(self, params):
|
||||||
|
if not params.get('resistor_values'):
|
||||||
|
raise ConfigError('Mandatory parameter "resistor_values" is not set.')
|
||||||
|
if params.get('labels'):
|
||||||
|
if len(params.get('labels')) != len(params.get('resistor_values')):
|
||||||
|
msg = 'Number of DAQ port labels does not match the number of resistor values.'
|
||||||
|
raise ConfigError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyProbeBackend(EnergyInstrumentBackend):
|
||||||
|
|
||||||
|
name = 'energy_probe'
|
||||||
|
|
||||||
|
parameters = [
|
||||||
|
Parameter('resistor_values', kind=list_of_ints,
|
||||||
|
description="""
|
||||||
|
The values of resistors (in Ohms) across which the voltages
|
||||||
|
are measured on.
|
||||||
|
"""),
|
||||||
|
Parameter('labels', kind=list_of_strings,
|
||||||
|
description="""
|
||||||
|
'List of port labels. If specified, the length of the list
|
||||||
|
must match the length of ``resistor_values``.
|
||||||
|
"""),
|
||||||
|
Parameter('device_entry', kind=str, default='/dev/ttyACM0',
|
||||||
|
description="""
|
||||||
|
Path to /dev entry for the energy probe (it should be /dev/ttyACMx)
|
||||||
|
"""),
|
||||||
|
]
|
||||||
|
|
||||||
|
instrument = EnergyProbeInstrument
|
||||||
|
|
||||||
|
def validate_parameters(self, params):
|
||||||
|
if not params.get('resistor_values'):
|
||||||
|
raise ConfigError('Mandatory parameter "resistor_values" is not set.')
|
||||||
|
if params.get('labels'):
|
||||||
|
if len(params.get('labels')) != len(params.get('resistor_values')):
|
||||||
|
msg = 'Number of Energy Probe port labels does not match the number of resistor values.'
|
||||||
|
raise ConfigError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyMeasurement(Instrument):
|
||||||
|
|
||||||
|
name = 'energy_measurement'
|
||||||
|
|
||||||
|
description = """
|
||||||
|
This instrument is designed to be used as an interface to the various
|
||||||
|
energy measurement instruments located in devlib.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters = [
|
||||||
|
Parameter('instrument', kind=str, mandatory=True,
|
||||||
|
allowed_values=['daq', 'energy_probe'],
|
||||||
|
description="""
|
||||||
|
Specify the energy instrumentation to be enabled.
|
||||||
|
"""),
|
||||||
|
Parameter('instrument_parameters', kind=dict, default={},
|
||||||
|
description="""
|
||||||
|
Specify the parameters used to initialize the desired
|
||||||
|
instrumentation.
|
||||||
|
"""),
|
||||||
|
Parameter('sites', kind=list_or_string, default=[],
|
||||||
|
description="""
|
||||||
|
Specify which sites measurements should be collected
|
||||||
|
from, if not specified the measurements will be
|
||||||
|
collected for all available sites.
|
||||||
|
"""),
|
||||||
|
Parameter('kinds', kind=list_or_string, default=[],
|
||||||
|
description="""
|
||||||
|
Specify the kinds of measurements should be collected,
|
||||||
|
if not specified measurements will be
|
||||||
|
collected for all available kinds.
|
||||||
|
"""),
|
||||||
|
Parameter('channels', kind=list_or_string, default=[],
|
||||||
|
description="""
|
||||||
|
Specify the channels to be collected,
|
||||||
|
if not specified the measurements will be
|
||||||
|
collected for all available channels.
|
||||||
|
"""),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, target, loader=pluginloader, **kwargs):
|
||||||
|
super(EnergyMeasurement, self).__init__(target, **kwargs)
|
||||||
|
self.instrumentation = None
|
||||||
|
self.measurement_csv = None
|
||||||
|
self.loader = loader
|
||||||
|
self.backend = self.loader.get_plugin(self.instrument)
|
||||||
|
self.params = {}
|
||||||
|
|
||||||
|
if self.backend.instrument.mode != CONTINUOUS:
|
||||||
|
msg = '{} instrument does not support continuous measurement collection'
|
||||||
|
raise ConfigError(msg.format(self.instrument))
|
||||||
|
|
||||||
|
supported_params = self.backend.get_parameters()
|
||||||
|
for name, value in supported_params.iteritems():
|
||||||
|
if name in self.instrument_parameters:
|
||||||
|
self.params[name] = self.instrument_parameters[name]
|
||||||
|
elif value.default:
|
||||||
|
self.params[name] = value.default
|
||||||
|
self.backend.validate_parameters(self.params)
|
||||||
|
|
||||||
|
def initialize(self, context):
|
||||||
|
self.instrumentation = self.backend.instrument(self.target, **self.params)
|
||||||
|
|
||||||
|
for channel in self.channels:
|
||||||
|
if not self.instrumentation.get_channels(channel):
|
||||||
|
raise ConfigError('No channels found for "{}"'.format(channel))
|
||||||
|
|
||||||
|
def setup(self, context):
|
||||||
|
self.instrumentation.reset(sites=self.sites,
|
||||||
|
kinds=self.kinds,
|
||||||
|
channels=self.channels)
|
||||||
|
|
||||||
|
def start(self, context):
|
||||||
|
self.instrumentation.start()
|
||||||
|
|
||||||
|
def stop(self, context):
|
||||||
|
self.instrumentation.stop()
|
||||||
|
|
||||||
|
def update_result(self, context):
|
||||||
|
outfile = os.path.join(context.output_directory, 'energy_instrument_output.csv')
|
||||||
|
self.measurement_csv = self.instrumentation.get_data(outfile)
|
||||||
|
context.add_artifact('energy_instrument_output', outfile, 'data')
|
||||||
|
self.extract_metrics(context)
|
||||||
|
|
||||||
|
def extract_metrics(self, context):
|
||||||
|
measurements = self.measurement_csv.itermeasurements()
|
||||||
|
energy_results = defaultdict(dict)
|
||||||
|
power_results = defaultdict(int)
|
||||||
|
|
||||||
|
for count, row in enumerate(measurements):
|
||||||
|
for entry in row:
|
||||||
|
channel = entry.channel
|
||||||
|
if channel.kind == 'energy':
|
||||||
|
if count == 0:
|
||||||
|
energy_results[channel.site]['start'] = entry.value
|
||||||
|
else:
|
||||||
|
energy_results[channel.site]['end'] = entry.value
|
||||||
|
elif channel.kind == 'power':
|
||||||
|
power_results[channel.site] += entry.value
|
||||||
|
|
||||||
|
for site in energy_results:
|
||||||
|
total_energy = energy_results[site]['end'] - energy_results[site]['start']
|
||||||
|
context.add_metric('{}_energy'.format(site), total_energy, 'joules')
|
||||||
|
for site in power_results:
|
||||||
|
power = power_results[site] / count + 1 #pylint: disable=undefined-loop-variable
|
||||||
|
context.add_metric('{}_power'.format(site), power, 'watts')
|
@ -85,18 +85,18 @@ class SysfsExtractor(Instrument):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def initialize(self, context):
|
def initialize(self, context):
|
||||||
if not self.device.is_rooted and self.use_tmpfs: # pylint: disable=access-member-before-definition
|
if not self.target.is_rooted and self.use_tmpfs: # pylint: disable=access-member-before-definition
|
||||||
raise ConfigError('use_tempfs must be False for an unrooted device.')
|
raise ConfigError('use_tempfs must be False for an unrooted device.')
|
||||||
elif self.use_tmpfs is None: # pylint: disable=access-member-before-definition
|
elif self.use_tmpfs is None: # pylint: disable=access-member-before-definition
|
||||||
self.use_tmpfs = self.device.is_rooted
|
self.use_tmpfs = self.target.is_rooted
|
||||||
|
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
self.on_device_before = self.device.path.join(self.tmpfs_mount_point, 'before')
|
self.on_device_before = self.target.path.join(self.tmpfs_mount_point, 'before')
|
||||||
self.on_device_after = self.device.path.join(self.tmpfs_mount_point, 'after')
|
self.on_device_after = self.target.path.join(self.tmpfs_mount_point, 'after')
|
||||||
|
|
||||||
if not self.device.file_exists(self.tmpfs_mount_point):
|
if not self.target.file_exists(self.tmpfs_mount_point):
|
||||||
self.device.execute('mkdir -p {}'.format(self.tmpfs_mount_point), as_root=True)
|
self.target.execute('mkdir -p {}'.format(self.tmpfs_mount_point), as_root=True)
|
||||||
self.device.execute(self.mount_command.format(self.tmpfs_size, self.tmpfs_mount_point),
|
self.target.execute(self.mount_command.format(self.tmpfs_size, self.tmpfs_mount_point),
|
||||||
as_root=True)
|
as_root=True)
|
||||||
|
|
||||||
def setup(self, context):
|
def setup(self, context):
|
||||||
@ -116,62 +116,62 @@ class SysfsExtractor(Instrument):
|
|||||||
|
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
for d in self.paths:
|
for d in self.paths:
|
||||||
before_dir = self.device.path.join(self.on_device_before,
|
before_dir = self.target.path.join(self.on_device_before,
|
||||||
self.device.path.dirname(as_relative(d)))
|
self.target.path.dirname(as_relative(d)))
|
||||||
after_dir = self.device.path.join(self.on_device_after,
|
after_dir = self.target.path.join(self.on_device_after,
|
||||||
self.device.path.dirname(as_relative(d)))
|
self.target.path.dirname(as_relative(d)))
|
||||||
if self.device.file_exists(before_dir):
|
if self.target.file_exists(before_dir):
|
||||||
self.device.execute('rm -rf {}'.format(before_dir), as_root=True)
|
self.target.execute('rm -rf {}'.format(before_dir), as_root=True)
|
||||||
self.device.execute('mkdir -p {}'.format(before_dir), as_root=True)
|
self.target.execute('mkdir -p {}'.format(before_dir), as_root=True)
|
||||||
if self.device.file_exists(after_dir):
|
if self.target.file_exists(after_dir):
|
||||||
self.device.execute('rm -rf {}'.format(after_dir), as_root=True)
|
self.target.execute('rm -rf {}'.format(after_dir), as_root=True)
|
||||||
self.device.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):
|
def slow_start(self, context):
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
for d in self.paths:
|
for d in self.paths:
|
||||||
dest_dir = self.device.path.join(self.on_device_before, as_relative(d))
|
dest_dir = self.target.path.join(self.on_device_before, as_relative(d))
|
||||||
if '*' in dest_dir:
|
if '*' in dest_dir:
|
||||||
dest_dir = self.device.path.dirname(dest_dir)
|
dest_dir = self.target.path.dirname(dest_dir)
|
||||||
self.device.execute('{} cp -Hr {} {}'.format(self.device.busybox, d, dest_dir),
|
self.target.execute('{} cp -Hr {} {}'.format(self.target.busybox, d, dest_dir),
|
||||||
as_root=True, check_exit_code=False)
|
as_root=True, check_exit_code=False)
|
||||||
else: # not rooted
|
else: # not rooted
|
||||||
for dev_dir, before_dir, _, _ in self.device_and_host_paths:
|
for dev_dir, before_dir, _, _ in self.device_and_host_paths:
|
||||||
self.device.pull(dev_dir, before_dir)
|
self.target.pull(dev_dir, before_dir)
|
||||||
|
|
||||||
def slow_stop(self, context):
|
def slow_stop(self, context):
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
for d in self.paths:
|
for d in self.paths:
|
||||||
dest_dir = self.device.path.join(self.on_device_after, as_relative(d))
|
dest_dir = self.target.path.join(self.on_device_after, as_relative(d))
|
||||||
if '*' in dest_dir:
|
if '*' in dest_dir:
|
||||||
dest_dir = self.device.path.dirname(dest_dir)
|
dest_dir = self.target.path.dirname(dest_dir)
|
||||||
self.device.execute('{} cp -Hr {} {}'.format(self.device.busybox, d, dest_dir),
|
self.target.execute('{} cp -Hr {} {}'.format(self.target.busybox, d, dest_dir),
|
||||||
as_root=True, check_exit_code=False)
|
as_root=True, check_exit_code=False)
|
||||||
else: # not using tmpfs
|
else: # not using tmpfs
|
||||||
for dev_dir, _, after_dir, _ in self.device_and_host_paths:
|
for dev_dir, _, after_dir, _ in self.device_and_host_paths:
|
||||||
self.device.pull(dev_dir, after_dir)
|
self.target.pull(dev_dir, after_dir)
|
||||||
|
|
||||||
def update_result(self, context):
|
def update_result(self, context):
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
on_device_tarball = self.device.path.join(self.device.working_directory, self.tarname)
|
on_device_tarball = self.target.path.join(self.target.working_directory, self.tarname)
|
||||||
on_host_tarball = self.device.path.join(context.output_directory, self.tarname)
|
on_host_tarball = self.target.path.join(context.output_directory, self.tarname)
|
||||||
self.device.execute('{} tar czf {} -C {} .'.format(self.device.busybox,
|
self.target.execute('{} tar czf {} -C {} .'.format(self.target.busybox,
|
||||||
on_device_tarball,
|
on_device_tarball,
|
||||||
self.tmpfs_mount_point),
|
self.tmpfs_mount_point),
|
||||||
as_root=True)
|
as_root=True)
|
||||||
self.device.execute('chmod 0777 {}'.format(on_device_tarball), as_root=True)
|
self.target.execute('chmod 0777 {}'.format(on_device_tarball), as_root=True)
|
||||||
self.device.pull(on_device_tarball, on_host_tarball)
|
self.target.pull(on_device_tarball, on_host_tarball)
|
||||||
with tarfile.open(on_host_tarball, 'r:gz') as tf:
|
with tarfile.open(on_host_tarball, 'r:gz') as tf:
|
||||||
tf.extractall(context.output_directory)
|
tf.extractall(context.output_directory)
|
||||||
self.device.remove(on_device_tarball)
|
self.target.remove(on_device_tarball)
|
||||||
os.remove(on_host_tarball)
|
os.remove(on_host_tarball)
|
||||||
|
|
||||||
for paths in self.device_and_host_paths:
|
for paths in self.device_and_host_paths:
|
||||||
after_dir = paths[self.AFTER_PATH]
|
after_dir = paths[self.AFTER_PATH]
|
||||||
dev_dir = paths[self.DEVICE_PATH].strip('*') # remove potential trailing '*'
|
dev_dir = paths[self.DEVICE_PATH].strip('*') # remove potential trailing '*'
|
||||||
if (not os.listdir(after_dir) and
|
if (not os.listdir(after_dir) and
|
||||||
self.device.file_exists(dev_dir) and
|
self.target.file_exists(dev_dir) and
|
||||||
self.device.list_directory(dev_dir)):
|
self.target.list_directory(dev_dir)):
|
||||||
self.logger.error('sysfs files were not pulled from the device.')
|
self.logger.error('sysfs files were not pulled from the device.')
|
||||||
self.device_and_host_paths.remove(paths) # Path is removed to skip diffing it
|
self.device_and_host_paths.remove(paths) # Path is removed to skip diffing it
|
||||||
for _, before_dir, after_dir, diff_dir in self.device_and_host_paths:
|
for _, before_dir, after_dir, diff_dir in self.device_and_host_paths:
|
||||||
@ -183,19 +183,19 @@ class SysfsExtractor(Instrument):
|
|||||||
def finalize(self, context):
|
def finalize(self, context):
|
||||||
if self.use_tmpfs:
|
if self.use_tmpfs:
|
||||||
try:
|
try:
|
||||||
self.device.execute('umount {}'.format(self.tmpfs_mount_point), as_root=True)
|
self.target.execute('umount {}'.format(self.tmpfs_mount_point), as_root=True)
|
||||||
except (TargetError, CalledProcessError):
|
except (TargetError, CalledProcessError):
|
||||||
# assume a directory but not mount point
|
# assume a directory but not mount point
|
||||||
pass
|
pass
|
||||||
self.device.execute('rm -rf {}'.format(self.tmpfs_mount_point),
|
self.target.execute('rm -rf {}'.format(self.tmpfs_mount_point),
|
||||||
as_root=True, check_exit_code=False)
|
as_root=True, check_exit_code=False)
|
||||||
|
|
||||||
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.device.path.join(self.device.working_directory, 'temp-fs')
|
self.tmpfs_mount_point = self.target.path.join(self.target.working_directory, 'temp-fs')
|
||||||
|
|
||||||
def _local_dir(self, directory):
|
def _local_dir(self, directory):
|
||||||
return os.path.dirname(as_relative(directory).replace(self.device.path.sep, os.sep))
|
return os.path.dirname(as_relative(directory).replace(self.target.path.sep, os.sep))
|
||||||
|
|
||||||
|
|
||||||
class ExecutionTimeInstrument(Instrument):
|
class ExecutionTimeInstrument(Instrument):
|
||||||
|
@ -40,13 +40,13 @@ class TraceCmdInstrument(Instrument):
|
|||||||
|
|
||||||
name = 'trace-cmd'
|
name = 'trace-cmd'
|
||||||
description = """
|
description = """
|
||||||
trace-cmd is an instrument which interacts with Ftrace Linux kernel internal
|
trace-cmd is an instrument which interacts with ftrace Linux kernel internal
|
||||||
tracer
|
tracer
|
||||||
|
|
||||||
From trace-cmd man page:
|
From trace-cmd man page:
|
||||||
|
|
||||||
trace-cmd command interacts with the Ftrace tracer that is built inside the
|
trace-cmd command interacts with the ftrace tracer that is built inside the
|
||||||
Linux kernel. It interfaces with the Ftrace specific files found in the
|
Linux kernel. It interfaces with the ftrace specific files found in the
|
||||||
debugfs file system under the tracing directory.
|
debugfs file system under the tracing directory.
|
||||||
|
|
||||||
trace-cmd reads a list of events it will trace, which can be specified in
|
trace-cmd reads a list of events it will trace, which can be specified in
|
||||||
@ -54,13 +54,8 @@ class TraceCmdInstrument(Instrument):
|
|||||||
|
|
||||||
trace_events = ['irq*', 'power*']
|
trace_events = ['irq*', 'power*']
|
||||||
|
|
||||||
If no event is specified in the config file, trace-cmd traces the following
|
If no event is specified, a default set of events that are generally considered useful
|
||||||
events:
|
for debugging/profiling purposes will be enabled.
|
||||||
|
|
||||||
- sched*
|
|
||||||
- irq*
|
|
||||||
- power*
|
|
||||||
- cpufreq_interactive*
|
|
||||||
|
|
||||||
The list of available events can be obtained by rooting and running the
|
The list of available events can be obtained by rooting and running the
|
||||||
following command line on the device ::
|
following command line on the device ::
|
||||||
@ -93,13 +88,17 @@ class TraceCmdInstrument(Instrument):
|
|||||||
is happening in each case from trace-cmd documentation:
|
is happening in each case from trace-cmd documentation:
|
||||||
https://lwn.net/Articles/341902/.
|
https://lwn.net/Articles/341902/.
|
||||||
|
|
||||||
This instrument comes with an Android trace-cmd binary that will be copied
|
This instrument comes with an trace-cmd binary that will be copied and used
|
||||||
and used on the device, however post-processing will be done on-host and
|
on the device, however post-processing will be, by default, done on-host and you must
|
||||||
you must have trace-cmd installed and in your path. On Ubuntu systems, this
|
have trace-cmd installed and in your path. On Ubuntu systems, this may be
|
||||||
may be done with::
|
done with::
|
||||||
|
|
||||||
sudo apt-get install trace-cmd
|
sudo apt-get install trace-cmd
|
||||||
|
|
||||||
|
Alternatively, you may set ``report_on_target`` parameter to ``True`` to enable on-target
|
||||||
|
processing (this is useful when running on non-Linux hosts, but is likely to take longer
|
||||||
|
and may fail on particularly resource-constrained targets).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parameters = [
|
parameters = [
|
||||||
@ -114,7 +113,7 @@ class TraceCmdInstrument(Instrument):
|
|||||||
Parameter('functions', kind=list_of_strings,
|
Parameter('functions', kind=list_of_strings,
|
||||||
global_alias='trace_functions',
|
global_alias='trace_functions',
|
||||||
description="""
|
description="""
|
||||||
Specifies the list of functions to be traced.
|
Specifies the list of functions to be traced.
|
||||||
"""),
|
"""),
|
||||||
Parameter('buffer_size', kind=int, default=None,
|
Parameter('buffer_size', kind=int, default=None,
|
||||||
global_alias='trace_buffer_size',
|
global_alias='trace_buffer_size',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user