blob: f6c34b30638abc9a943f9be3bf7b15ebba082a9f [file] [log] [blame]
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2015, ARM Limited and contributors.
#
# 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.
#
import glob
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import pylab as pl
import re
import sys
import trappy
# Configure logging
import logging
# Regexp to match an rt-app generated logfile
TASK_NAME_RE = re.compile('.*\/rt-app-(.+)-[0-9]+.log')
class PerfAnalysis(object):
def __init__(self, datadir, tasks=None):
# Dataframe of all tasks performance data
self.perf_data = {}
# Folder containing all rt-app data
self.datadir = None
# Load performance data generated by rt-app workloads
self.__loadRTAData(datadir, tasks)
# Keep track of the datadir from where data have been loaded
if len(self.perf_data) == 0:
raise ValueError('No performance data found on folder [{0:s}]'\
.format(datadir))
self.datadir = datadir
def __taskNameFromLog(self, logfile):
tname_match = re.search(TASK_NAME_RE, logfile)
if tname_match is None:
raise ValueError('The logfile [{0:s}] is not from rt-app'\
.format(logfile))
return tname_match.group(1)
def __logfileFromTaskName(self, taskname):
for logfile in glob.glob(
'{0:s}/rt-app-{1:s}.log'.format(self.datadir, taskname)):
return logfile
raise ValueError('No rt-app logfile found for task [{0:s}]'\
.format(taskname))
def tasks(self):
"""
Return the list of tasks for which performance data have been loaded
"""
if self.datadir is None:
raise ValueError("rt-app performance data not (yet) loaded")
return self.perf_data.keys()
def logfile(self, task):
"""
Return the logfile for the specified task
"""
if task not in self.perf_data:
raise ValueError('No logfile loaded for task [{0:s}]'\
.format(task))
return self.perf_data[task]['logfile']
def df(self, task):
"""
Return the PANDAS dataframe with the performance data for the
specified task
"""
if self.datadir is None:
raise ValueError("rt-app performance data not (yet) loaded")
if task not in self.perf_data:
raise ValueError('No dataframe loaded for task [{0:s}]'\
.format(task))
return self.perf_data[task]['df']
def __loadRTAData(self, datadir, tasks):
"""
Load peformance data of an rt-app workload
"""
if tasks is None:
# Lookup for all rt-app logfile into the specified datadir
for logfile in glob.glob('{0:s}/rt-app-*.log'.format(datadir)):
task_name = self.__taskNameFromLog(logfile)
self.perf_data[task_name] = {}
self.perf_data[task_name]['logfile'] = logfile
logging.debug('Found rt-app logfile for task [%s]', task_name)
else:
# Lookup for specified rt-app task logfile into specified datadir
for task in tasks:
logfile = self.__logfileFromTaskName(task)
self.perf_data[task_name] = {}
self.perf_data[task_name]['logfile'] = logfile
logging.debug('Found rt-app logfile for task [%s]', task_name)
# Load all the found logfile into a dataset
for task in self.perf_data.keys():
logging.debug('Loading dataframe for task [%s]...', task)
df = pd.read_table(self.logfile(task),
sep='\s+',
skiprows=1,
header=0,
usecols=[1,2,3,4,7,8,9,10],
names=[
'Cycles', 'Run' ,'Period', 'Timestamp',
'Slack', 'CRun', 'CPeriod', 'WKPLatency'
])
# Normalize time to [s] with origin on the first event
start_time = df['Timestamp'][0]/1e6
df['Time'] = df['Timestamp']/1e6 - start_time
df.set_index(['Time'], inplace=True)
# Add performance metrics column, performance is defined as:
# slack
# perf = -------------
# period - run
df['PerfIndex'] = df['Slack'] / (df['CPeriod'] - df['CRun'])
# Keep track of the loaded dataframe
self.perf_data[task]['df'] = df
def plotPerf(self, task, title=None):
"""
Plot the Latency/Slack and Performance data for the specified task
"""
# Grid
gs = gridspec.GridSpec(2, 2, height_ratios=[4,1], width_ratios=[3,1]);
gs.update(wspace=0.1, hspace=0.1);
# Figure
plt.figure(figsize=(16, 2*6));
if title:
plt.suptitle(title, y=.97, fontsize=16,
horizontalalignment='center');
# Plot: Slack and Latency
axes = plt.subplot(gs[0,0]);
axes.set_title('Task [{0:s}] (start) Latency and (completion) Slack'\
.format(task));
data = self.df(task)[['Slack', 'WKPLatency']]
data.plot(ax=axes, drawstyle='steps-post', style=['r', 'g']);
# axes.set_xlim(x_min, x_max);
axes.xaxis.set_visible(False);
# Plot: Performance
axes = plt.subplot(gs[1,0]);
axes.set_title('Task [{0:s}] Performance Index'.format(task));
data = self.df(task)[['PerfIndex',]]
data.plot(ax=axes, drawstyle='steps-post');
axes.set_ylim(0, 2);
# axes.set_xlim(x_min, x_max);
# Plot: Slack Histogram
axes = plt.subplot(gs[0:2,1]);
data = self.df(task)[['PerfIndex',]]
data.hist(bins=30, ax=axes, alpha=0.4);
# axes.set_xlim(x_min, x_max);
pindex_avg = data.mean()[0];
pindex_std = data.std()[0];
logging.info('PerfIndex, Task [%s] avg: %.2f, std: %.2f',
task, pindex_avg, pindex_std)
axes.axvline(pindex_avg, color='b', linestyle='--', linewidth=2);
# Save generated plots into datadir
figname = '{}/task_perf_{}.png'.format(self.datadir, task)
pl.savefig(figname, bbox_inches='tight')