blob: 31a53990d3f6b1a4fb6264fa77caf7367b4de95a [file] [log] [blame]
from __future__ import division
import csv
import os
import re
try:
import pandas as pd
except ImportError:
pd = None
from devlib import DerivedMeasurements, DerivedMetric, MeasurementsCsv, InstrumentChannel
from devlib.utils.rendering import gfxinfo_get_last_dump, VSYNC_INTERVAL
from devlib.utils.types import numeric
class DerivedFpsStats(DerivedMeasurements):
def __init__(self, drop_threshold=5, suffix=None, filename=None, outdir=None):
self.drop_threshold = drop_threshold
self.suffix = suffix
self.filename = filename
self.outdir = outdir
if (filename is None) and (suffix is None):
self.suffix = '-fps'
elif (filename is not None) and (suffix is not None):
raise ValueError('suffix and filename cannot be specified at the same time.')
if filename is not None and os.sep in filename:
raise ValueError('filename cannot be a path (cannot countain "{}"'.format(os.sep))
def process(self, measurements_csv):
if isinstance(measurements_csv, basestring):
measurements_csv = MeasurementsCsv(measurements_csv)
if pd is not None:
return self._process_with_pandas(measurements_csv)
return self._process_without_pandas(measurements_csv)
def _get_csv_file_name(self, frames_file):
outdir = self.outdir or os.path.dirname(frames_file)
if self.filename:
return os.path.join(outdir, self.filename)
frames_basename = os.path.basename(frames_file)
rest, ext = os.path.splitext(frames_basename)
csv_basename = rest + self.suffix + ext
return os.path.join(outdir, csv_basename)
class DerivedGfxInfoStats(DerivedFpsStats):
@staticmethod
def process_raw(filepath, *args):
metrics = []
dump = gfxinfo_get_last_dump(filepath)
seen_stats = False
for line in dump.split('\n'):
if seen_stats and not line.strip():
break
elif line.startswith('Janky frames:'):
text = line.split(': ')[-1]
val_text, pc_text = text.split('(')
metrics.append(DerivedMetric('janks', numeric(val_text.strip()), 'count'))
metrics.append(DerivedMetric('janks_pc', numeric(pc_text[:-3]), 'percent'))
elif ' percentile: ' in line:
ptile, val_text = line.split(' percentile: ')
name = 'render_time_{}_ptile'.format(ptile)
value = numeric(val_text.strip()[:-2])
metrics.append(DerivedMetric(name, value, 'time_ms'))
elif line.startswith('Number '):
name_text, val_text = line.strip().split(': ')
name = name_text[7:].lower().replace(' ', '_')
value = numeric(val_text)
metrics.append(DerivedMetric(name, value, 'count'))
else:
continue
seen_stats = True
return metrics
def _process_without_pandas(self, measurements_csv):
per_frame_fps = []
start_vsync, end_vsync = None, None
frame_count = 0
for frame_data in measurements_csv.iter_values():
if frame_data.Flags_flags != 0:
continue
frame_count += 1
if start_vsync is None:
start_vsync = frame_data.Vsync_time_us
end_vsync = frame_data.Vsync_time_us
frame_time = frame_data.FrameCompleted_time_us - frame_data.IntendedVsync_time_us
pff = 1e9 / frame_time
if pff > self.drop_threshold:
per_frame_fps.append([pff])
if frame_count:
duration = end_vsync - start_vsync
fps = (1e9 * frame_count) / float(duration)
else:
duration = 0
fps = 0
csv_file = self._get_csv_file_name(measurements_csv.path)
with open(csv_file, 'wb') as wfh:
writer = csv.writer(wfh)
writer.writerow(['fps'])
writer.writerows(per_frame_fps)
return [DerivedMetric('fps', fps, 'fps'),
DerivedMetric('total_frames', frame_count, 'frames'),
MeasurementsCsv(csv_file)]
def _process_with_pandas(self, measurements_csv):
data = pd.read_csv(measurements_csv.path)
data = data[data.Flags_flags == 0]
frame_time = data.FrameCompleted_time_us - data.IntendedVsync_time_us
per_frame_fps = (1e9 / frame_time)
keep_filter = per_frame_fps > self.drop_threshold
per_frame_fps = per_frame_fps[keep_filter]
per_frame_fps.name = 'fps'
frame_count = data.index.size
if frame_count > 1:
duration = data.Vsync_time_us.iloc[-1] - data.Vsync_time_us.iloc[0]
fps = (1e9 * frame_count) / float(duration)
else:
duration = 0
fps = 0
csv_file = self._get_csv_file_name(measurements_csv.path)
per_frame_fps.to_csv(csv_file, index=False, header=True)
return [DerivedMetric('fps', fps, 'fps'),
DerivedMetric('total_frames', frame_count, 'frames'),
MeasurementsCsv(csv_file)]