1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2024-10-06 19:01:15 +01:00

Merge pull request #133 from mcgeagh/uxperf

CpuUtilisationTimeline added. This now will generate cpu utilisation …
This commit is contained in:
Sebastian Goscik 2016-04-14 14:05:58 +01:00
commit df8ef6be6b
2 changed files with 73 additions and 2 deletions

20
wlauto/result_processors/cpustate.py Normal file → Executable file
View File

@ -102,7 +102,13 @@ class CpuStatesProcessor(ResultProcessor):
Create a CSV with the timeline of core power states over the course of the run
as well as the usual stats reports.
"""),
Parameter('create_utilization_timeline', kind=bool, default=False,
description="""
Create a CSV with the timeline of cpu(s) utilisation over the course of the run
as well as the usual stats reports.
The values generated are floating point numbers, normalised based on the maximum
frequency of the cluster.
"""),
]
def validate(self):
@ -130,6 +136,7 @@ class CpuStatesProcessor(ResultProcessor):
self.idle_state_names = [idle_states[i] for i in sorted(idle_states.keys())]
self.num_idle_states = len(self.idle_state_names)
self.iteration_reports = OrderedDict()
self.max_freq_list = []
# priority -19: just higher than the slow_start of instrumentation
signal.connect(self.set_initial_state, signal.BEFORE_WORKLOAD_EXECUTION, priority=-19)
@ -139,12 +146,17 @@ class CpuStatesProcessor(ResultProcessor):
# Write initial frequencies into the trace.
# NOTE: this assumes per-cluster DVFS, that is valid for devices that
# currently exist. This will need to be updated for per-CPU DFS.
# pylint: disable=attribute-defined-outside-init
self.logger.debug('Writing initial frequencies into trace...')
device = context.device
cluster_freqs = {}
cluster_max_freqs = {}
self.max_freq_list = []
for c in unique(device.core_clusters):
cluster_freqs[c] = device.get_cluster_cur_frequency(c)
cluster_max_freqs[c] = device.get_cluster_max_frequency(c)
for i, c in enumerate(device.core_clusters):
self.max_freq_list.append(cluster_max_freqs[c])
entry = 'CPU {} FREQUENCY: {} kHZ'.format(i, cluster_freqs[c])
device.set_sysfile_value('/sys/kernel/debug/tracing/trace_marker',
entry, verify=False)
@ -165,6 +177,10 @@ class CpuStatesProcessor(ResultProcessor):
timeline_csv_file = os.path.join(context.output_directory, 'power_states.csv')
else:
timeline_csv_file = None
if self.create_utilization_timeline:
cpu_utilisation = os.path.join(context.output_directory, 'cpu_utilisation.csv')
else:
cpu_utilisation = None
parallel_report, powerstate_report = report_power_stats( # pylint: disable=unbalanced-tuple-unpacking
trace_file=trace.path,
idle_state_names=self.idle_state_names,
@ -175,6 +191,8 @@ class CpuStatesProcessor(ResultProcessor):
first_system_state=self.first_system_state,
use_ratios=self.use_ratios,
timeline_csv_file=timeline_csv_file,
cpu_utilisation=cpu_utilisation,
max_freq_list=self.max_freq_list,
)
if parallel_report is None:
self.logger.warning('No power state reports generated; are power '

55
wlauto/utils/power.py Normal file → Executable file
View File

@ -515,6 +515,39 @@ class PowerStateStatsReport(object):
for s in stats])
class CpuUtilisationTimeline(object):
def __init__(self, filepath, core_names, max_freq_list):
self.filepath = filepath
self._wfh = open(filepath, 'w')
self.writer = csv.writer(self._wfh)
if core_names:
headers = ['ts'] + ['{} CPU{}'.format(c, i)
for i, c in enumerate(core_names)]
self.writer.writerow(headers)
self._max_freq_list = max_freq_list
def update(self, timestamp, core_states): # NOQA
row = [timestamp]
for core, [idle_state, frequency] in enumerate(core_states):
if idle_state == -1:
if frequency == UNKNOWN_FREQUENCY:
frequency = 0
elif idle_state is None:
frequency = 0
else:
frequency = 0
if core < len(self._max_freq_list):
frequency /= float(self._max_freq_list[core])
row.append(frequency)
else:
logger.warning('Unable to detect max frequency for this core. Cannot log utilisation value')
self.writer.writerow(row)
def report(self):
self._wfh.close()
def build_idle_domains(core_clusters, # NOQA
num_states,
first_cluster_state=None,
@ -577,7 +610,8 @@ def build_idle_domains(core_clusters, # NOQA
def report_power_stats(trace_file, idle_state_names, core_names, core_clusters,
num_idle_states, first_cluster_state=sys.maxint,
first_system_state=sys.maxint, use_ratios=False,
timeline_csv_file=None, filter_trace=False):
timeline_csv_file=None, filter_trace=False,
cpu_utilisation=None, max_freq_list=None):
# pylint: disable=too-many-locals
trace = TraceCmdTrace(filter_markers=filter_trace)
ps_processor = PowerStateProcessor(core_clusters,
@ -592,6 +626,11 @@ def report_power_stats(trace_file, idle_state_names, core_names, core_clusters,
if timeline_csv_file:
reporters.append(PowerStateTimeline(timeline_csv_file,
core_names, idle_state_names))
if cpu_utilisation:
if max_freq_list:
reporters.append(CpuUtilisationTimeline(cpu_utilisation, core_names, max_freq_list))
else:
logger.warning('Maximum frequencies not found. Cannot normalise. Skipping CPU Utilisation Timeline')
event_stream = trace.parse(trace_file, names=['cpu_idle', 'cpu_frequency', 'print'])
transition_stream = stream_cpu_power_transitions(event_stream)
@ -624,6 +663,8 @@ def main():
use_ratios=args.ratios,
timeline_csv_file=args.timeline_file,
filter_trace=(not args.no_trace_filter),
cpu_utilisation=args.cpu_utilisation,
max_freq_list=args.max_freq_list,
)
parallel_report.write(os.path.join(args.output_directory, 'parallel.csv'))
powerstate_report.write(os.path.join(args.output_directory, 'cpustate.csv'))
@ -698,6 +739,18 @@ def parse_arguments(): # NOQA
A timeline of core power states will be written to the specified file in
CSV format.
''')
parser.add_argument('-u', '--cpu-utilisation', metavar='FILE',
help='''
A timeline of cpu(s) utilisation will be written to the specified file in
CSV format.
''')
parser.add_argument('-m', '--max-freq-list', action=SplitListAction, default=[],
help='''
Comma-separated list of core maximum frequencies for the device on which
the trace was collected.
Only required if --cpu-utilisation is set.
This is used to normalise the frequencies to obtain percentage utilisation.
''')
args = parser.parse_args()