mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-10-31 15:12:25 +00: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:
		| @@ -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 | ||||||
|  |  | ||||||
|  |                 if self.generate_csv: | ||||||
|  |                     per_frame_fps.to_csv(self.fps_outfile, index=False, header=True) | ||||||
|  |                     context.add_artifact('fps', path='fps.csv', kind='data') | ||||||
|  |  | ||||||
|             context.result.add_metric('FPS', fps) |             context.result.add_metric('FPS', fps) | ||||||
|             context.result.add_metric('frame_count', frame_count) |             context.result.add_metric('frame_count', frame_count) | ||||||
|             context.result.add_metric('janks', janks) |             context.result.add_metric('janks', janks) | ||||||
|             context.result.add_metric('not_at_vsync', not_at_vsync) |             context.result.add_metric('not_at_vsync', not_at_vsync) | ||||||
|  |  | ||||||
|                 if self.generate_csv: |  | ||||||
|                     per_frame_fps.to_csv(self.fps_outfile, index=False, header=True) |  | ||||||
|                     context.add_artifact('fps', path='fps.csv', kind='data') |  | ||||||
|  |  | ||||||
|     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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user