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

Add dumpsys_period parameter to fps instrument

Add a new parameter to fps instrument to specify the time period between
calls to ``dumpsys SurfaceFlinger --latency`` in seconds when collecting
frame data. A lower value improves the granularity of timings when
recording actions for UX Performance metrics.
This commit is contained in:
John Richardson 2016-07-25 10:10:19 +01:00
parent aa2d187c4d
commit 625a3a39a5

View File

@ -111,6 +111,18 @@ class FpsInstrument(Instrument):
a content crash. E.g. a value of ``0.75`` means the number of actual frames counted is a a content crash. E.g. a value of ``0.75`` means the number of actual frames counted is a
quarter lower than expected, it will treated as a content crash. quarter lower than expected, it will treated as a content crash.
"""), """),
Parameter('dumpsys_period', kind=float, default=2, constraint=lambda x: x > 0,
description="""
Specifies the time period between calls to ``dumpsys SurfaceFlinger --latency`` in
seconds when collecting frame data. Using a lower value improves the granularity
of timings when recording actions that take a short time to complete. Note, this
will produce duplicate frame data in the raw dumpsys output, however, this is
filtered out in frames.csv. It may also affect the overall load on the system.
The default value of 2 seconds corresponds with the NUM_FRAME_RECORDS in
android/services/surfaceflinger/FrameTracker.h (as of the time of writing
currently 128) and a frame rate of 60 fps that is applicable to most devices.
"""),
] ]
clear_command = 'dumpsys SurfaceFlinger --latency-clear ' clear_command = 'dumpsys SurfaceFlinger --latency-clear '
@ -135,7 +147,8 @@ class FpsInstrument(Instrument):
if hasattr(workload, 'view'): if hasattr(workload, 'view'):
self.fps_outfile = os.path.join(context.output_directory, 'fps.csv') self.fps_outfile = os.path.join(context.output_directory, 'fps.csv')
self.outfile = os.path.join(context.output_directory, 'frames.csv') self.outfile = os.path.join(context.output_directory, 'frames.csv')
self.collector = LatencyCollector(self.outfile, self.device, workload.view or '', self.keep_raw, self.logger) self.collector = LatencyCollector(self.outfile, self.device, workload.view or '',
self.keep_raw, self.logger, self.dumpsys_period)
self.device.execute(self.clear_command) self.device.execute(self.clear_command)
else: else:
self.logger.debug('Workload does not contain a view; disabling...') self.logger.debug('Workload does not contain a view; disabling...')
@ -153,21 +166,22 @@ class FpsInstrument(Instrument):
def update_result(self, context): def update_result(self, context):
if self.is_enabled: if self.is_enabled:
fps, frame_count, janks, not_at_vsync = float('nan'), 0, 0, 0
data = pd.read_csv(self.outfile) data = pd.read_csv(self.outfile)
if not data.empty: # pylint: disable=maybe-no-member if not data.empty: # pylint: disable=maybe-no-member
fp = FpsProcessor(data) fp = FpsProcessor(data)
per_frame_fps, metrics = fp.process(self.collector.refresh_period, self.drop_threshold) per_frame_fps, metrics = fp.process(self.collector.refresh_period, self.drop_threshold)
fps, frame_count, janks, not_at_vsync = metrics fps, frame_count, janks, not_at_vsync = metrics
context.result.add_metric('FPS', fps)
context.result.add_metric('frame_count', frame_count)
context.result.add_metric('janks', janks)
context.result.add_metric('not_at_vsync', not_at_vsync)
if self.generate_csv: if self.generate_csv:
per_frame_fps.to_csv(self.fps_outfile, index=False, header=True) per_frame_fps.to_csv(self.fps_outfile, index=False, header=True)
context.add_artifact('fps', path='fps.csv', kind='data') context.add_artifact('fps', path='fps.csv', kind='data')
context.result.add_metric('FPS', fps)
context.result.add_metric('frame_count', frame_count)
context.result.add_metric('janks', janks)
context.result.add_metric('not_at_vsync', not_at_vsync)
def slow_update_result(self, context): def slow_update_result(self, context):
result = context.result result = context.result
if self.crash_check and result.has_metric('execution_time'): if self.crash_check and result.has_metric('execution_time'):
@ -192,16 +206,17 @@ class LatencyCollector(threading.Thread):
# At the time of writing, this was hard-coded to 128. So at 60 fps # At the time of writing, this was hard-coded to 128. So at 60 fps
# (and there is no reason to go above that, as it matches vsync rate # (and there is no reason to go above that, as it matches vsync rate
# on pretty much all phones), there is just over 2 seconds' worth of # on pretty much all phones), there is just over 2 seconds' worth of
# frames in there. Hence the sleep time of 2 seconds between dumps. # frames in there. Hence the default sleep time of 2 seconds between dumps.
#command_template = 'while (true); do dumpsys SurfaceFlinger --latency {}; sleep 2; done' #command_template = 'while (true); do dumpsys SurfaceFlinger --latency {}; sleep 2; done'
command_template = 'dumpsys SurfaceFlinger --latency {}' command_template = 'dumpsys SurfaceFlinger --latency {}'
def __init__(self, outfile, device, activities, keep_raw, logger): def __init__(self, outfile, device, activities, keep_raw, logger, dumpsys_period):
super(LatencyCollector, self).__init__() super(LatencyCollector, self).__init__()
self.outfile = outfile self.outfile = outfile
self.device = device self.device = device
self.keep_raw = keep_raw self.keep_raw = keep_raw
self.logger = logger self.logger = logger
self.dumpsys_period = dumpsys_period
self.stop_signal = threading.Event() self.stop_signal = threading.Event()
self.frames = [] self.frames = []
self.last_ready_time = 0 self.last_ready_time = 0
@ -226,7 +241,7 @@ class LatencyCollector(threading.Thread):
for activity in self.activities: for activity in self.activities:
if activity in view_list: if activity in view_list:
wfh.write(self.device.execute(self.command_template.format(activity))) wfh.write(self.device.execute(self.command_template.format(activity)))
time.sleep(2) time.sleep(self.dumpsys_period)
finally: finally:
wfh.close() wfh.close()
# TODO: this can happen after the run during results processing # TODO: this can happen after the run during results processing