blob: 31a53990d3f6b1a4fb6264fa77caf7367b4de95a [file] [log] [blame]
Sergei Trofimovf6923152017-09-07 17:36:18 +01001from __future__ import division
2import csv
3import os
4import re
5
6try:
7 import pandas as pd
8except ImportError:
9 pd = None
10
11from devlib import DerivedMeasurements, DerivedMetric, MeasurementsCsv, InstrumentChannel
12from devlib.utils.rendering import gfxinfo_get_last_dump, VSYNC_INTERVAL
13from devlib.utils.types import numeric
14
15
16class DerivedFpsStats(DerivedMeasurements):
17
18 def __init__(self, drop_threshold=5, suffix=None, filename=None, outdir=None):
19 self.drop_threshold = drop_threshold
20 self.suffix = suffix
21 self.filename = filename
22 self.outdir = outdir
23 if (filename is None) and (suffix is None):
24 self.suffix = '-fps'
25 elif (filename is not None) and (suffix is not None):
26 raise ValueError('suffix and filename cannot be specified at the same time.')
27 if filename is not None and os.sep in filename:
28 raise ValueError('filename cannot be a path (cannot countain "{}"'.format(os.sep))
29
30 def process(self, measurements_csv):
31 if isinstance(measurements_csv, basestring):
32 measurements_csv = MeasurementsCsv(measurements_csv)
33 if pd is not None:
34 return self._process_with_pandas(measurements_csv)
35 return self._process_without_pandas(measurements_csv)
36
37 def _get_csv_file_name(self, frames_file):
38 outdir = self.outdir or os.path.dirname(frames_file)
39 if self.filename:
40 return os.path.join(outdir, self.filename)
41
42 frames_basename = os.path.basename(frames_file)
43 rest, ext = os.path.splitext(frames_basename)
44 csv_basename = rest + self.suffix + ext
45 return os.path.join(outdir, csv_basename)
46
47
48class DerivedGfxInfoStats(DerivedFpsStats):
49
50 @staticmethod
51 def process_raw(filepath, *args):
52 metrics = []
53 dump = gfxinfo_get_last_dump(filepath)
54 seen_stats = False
55 for line in dump.split('\n'):
56 if seen_stats and not line.strip():
57 break
58 elif line.startswith('Janky frames:'):
59 text = line.split(': ')[-1]
60 val_text, pc_text = text.split('(')
61 metrics.append(DerivedMetric('janks', numeric(val_text.strip()), 'count'))
62 metrics.append(DerivedMetric('janks_pc', numeric(pc_text[:-3]), 'percent'))
63 elif ' percentile: ' in line:
64 ptile, val_text = line.split(' percentile: ')
65 name = 'render_time_{}_ptile'.format(ptile)
66 value = numeric(val_text.strip()[:-2])
67 metrics.append(DerivedMetric(name, value, 'time_ms'))
68 elif line.startswith('Number '):
69 name_text, val_text = line.strip().split(': ')
70 name = name_text[7:].lower().replace(' ', '_')
71 value = numeric(val_text)
72 metrics.append(DerivedMetric(name, value, 'count'))
73 else:
74 continue
75 seen_stats = True
76 return metrics
77
78 def _process_without_pandas(self, measurements_csv):
79 per_frame_fps = []
80 start_vsync, end_vsync = None, None
81 frame_count = 0
82
83 for frame_data in measurements_csv.iter_values():
84 if frame_data.Flags_flags != 0:
85 continue
86 frame_count += 1
87
88 if start_vsync is None:
89 start_vsync = frame_data.Vsync_time_us
90 end_vsync = frame_data.Vsync_time_us
91
92 frame_time = frame_data.FrameCompleted_time_us - frame_data.IntendedVsync_time_us
93 pff = 1e9 / frame_time
94 if pff > self.drop_threshold:
95 per_frame_fps.append([pff])
96
97 if frame_count:
98 duration = end_vsync - start_vsync
99 fps = (1e9 * frame_count) / float(duration)
100 else:
101 duration = 0
102 fps = 0
103
104 csv_file = self._get_csv_file_name(measurements_csv.path)
105 with open(csv_file, 'wb') as wfh:
106 writer = csv.writer(wfh)
107 writer.writerow(['fps'])
108 writer.writerows(per_frame_fps)
109
110 return [DerivedMetric('fps', fps, 'fps'),
111 DerivedMetric('total_frames', frame_count, 'frames'),
112 MeasurementsCsv(csv_file)]
113
114 def _process_with_pandas(self, measurements_csv):
115 data = pd.read_csv(measurements_csv.path)
116 data = data[data.Flags_flags == 0]
117 frame_time = data.FrameCompleted_time_us - data.IntendedVsync_time_us
118 per_frame_fps = (1e9 / frame_time)
119 keep_filter = per_frame_fps > self.drop_threshold
120 per_frame_fps = per_frame_fps[keep_filter]
121 per_frame_fps.name = 'fps'
122
123 frame_count = data.index.size
124 if frame_count > 1:
125 duration = data.Vsync_time_us.iloc[-1] - data.Vsync_time_us.iloc[0]
126 fps = (1e9 * frame_count) / float(duration)
127 else:
128 duration = 0
129 fps = 0
130
131 csv_file = self._get_csv_file_name(measurements_csv.path)
132 per_frame_fps.to_csv(csv_file, index=False, header=True)
133
134 return [DerivedMetric('fps', fps, 'fps'),
135 DerivedMetric('total_frames', frame_count, 'frames'),
136 MeasurementsCsv(csv_file)]
137