| Index: tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py
|
| diff --git a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py
|
| index bd8c826d440c907e5b6e18975665143056d3d62f..fc9903249f76f40ae4f5ef7503992e4027245635 100644
|
| --- a/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py
|
| +++ b/tools/telemetry/telemetry/web_perf/metrics/rendering_stats.py
|
| @@ -6,6 +6,7 @@ import logging
|
| from operator import attrgetter
|
| from telemetry.page import page_measurement
|
| from telemetry.web_perf.metrics import rendering_frame
|
| +from collections import namedtuple
|
|
|
| # These are LatencyInfo component names indicating the various components
|
| # that the input event has travelled through.
|
| @@ -17,7 +18,12 @@ ORIGINAL_COMP_NAME = 'INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT'
|
| BEGIN_COMP_NAME = 'INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT'
|
| # This is when the input event has reached swap buffer.
|
| END_COMP_NAME = 'INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT'
|
| +# The 'step' for an event that is a gesture scroll update
|
| +GESTURE_SCROLL_UPDATE = 'GestureScrollUpdate'
|
| +# Wildcard used to match all step types
|
| +ALL_INPUT_EVENTS = ''
|
|
|
| +InputEventDetails = namedtuple('InputEventDetails','start_time latency')
|
|
|
| class NotEnoughFramesError(page_measurement.MeasurementFailure):
|
| def __init__(self, frame_count):
|
| @@ -31,7 +37,6 @@ class NotEnoughFramesError(page_measurement.MeasurementFailure):
|
| '- Pages that render extremely slow\n' +
|
| '- Pages that can\'t be scrolled')
|
|
|
| -
|
| def GetInputLatencyEvents(process, timeline_range):
|
| """Get input events' LatencyInfo from the process's trace buffer that are
|
| within the timeline_range.
|
| @@ -41,47 +46,114 @@ def GetInputLatencyEvents(process, timeline_range):
|
| its latency history.
|
|
|
| """
|
| +
|
| input_events = []
|
| if not process:
|
| return input_events
|
| for event in process.IterAllAsyncSlicesOfName('InputLatency'):
|
| +
|
| if event.start >= timeline_range.min and event.end <= timeline_range.max:
|
| for ss in event.sub_slices:
|
| if 'data' in ss.args:
|
| input_events.append(ss)
|
| +
|
| return input_events
|
|
|
|
|
| +def FindInputEventStartAndEnd(event):
|
| + """
|
| + Input event on different platforms uses different LatencyInfo component to
|
| + record its creation timestamp. We go through the following component list
|
| + to find the creation timestamp:
|
| + 1. INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT -- when event is created in OS
|
| + 2. INPUT_EVENT_LATENCY_UI_COMPONENT -- when event reaches Chrome
|
| + 3. INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT -- when event reaches RenderWidget
|
| + """
|
| +
|
| + data = event.args['data']
|
| +
|
| + if END_COMP_NAME in data:
|
| + end_time = data[END_COMP_NAME]['time']
|
| + if ORIGINAL_COMP_NAME in data:
|
| + start_time = data[ORIGINAL_COMP_NAME]['time']
|
| + elif UI_COMP_NAME in data:
|
| + start_time = data[UI_COMP_NAME]['time']
|
| + elif BEGIN_COMP_NAME in data:
|
| + start_time = data[BEGIN_COMP_NAME]['time']
|
| + else:
|
| + raise ValueError, 'LatencyInfo has no begin component'
|
| +
|
| + # Events are stored internal in microseconds, convert to ms
|
| +
|
| + start_time_ms = start_time / 1000.0
|
| + end_time_ms = end_time / 1000.0
|
| +
|
| + return True, start_time_ms, end_time_ms
|
| +
|
| + # Return failure and dummy values
|
| + return False, 0, 0
|
| +
|
| +
|
| def ComputeInputEventLatency(input_events):
|
| """ Compute the input event latency.
|
|
|
| Input event latency is the time from when the input event is created to
|
| when its resulted page is swap buffered.
|
| - Input event on differnt platforms uses different LatencyInfo component to
|
| - record its creation timestamp. We go through the following component list
|
| - to find the creation timestamp:
|
| - 1. INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT -- when event is created in OS
|
| - 2. INPUT_EVENT_LATENCY_UI_COMPONENT -- when event reaches Chrome
|
| - 3. INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT -- when event reaches RenderWidget
|
| -
|
| """
|
| input_event_latency = []
|
| for event in input_events:
|
| - data = event.args['data']
|
| - if END_COMP_NAME in data:
|
| - end_time = data[END_COMP_NAME]['time']
|
| - if ORIGINAL_COMP_NAME in data:
|
| - latency = end_time - data[ORIGINAL_COMP_NAME]['time']
|
| - elif UI_COMP_NAME in data:
|
| - latency = end_time - data[UI_COMP_NAME]['time']
|
| - elif BEGIN_COMP_NAME in data:
|
| - latency = end_time - data[BEGIN_COMP_NAME]['time']
|
| - else:
|
| - raise ValueError, 'LatencyInfo has no begin component'
|
| - input_event_latency.append(latency / 1000.0)
|
| +
|
| + event_exists, start_time, end_time = FindInputEventStartAndEnd(event)
|
| +
|
| + if event_exists:
|
| + latency = end_time - start_time
|
| + input_event_latency.append(latency)
|
| +
|
| return input_event_latency
|
|
|
|
|
| +def EventIsOfDesireType(event, requested_type):
|
| + """ Given an event and a type, return True if the event is of the requested
|
| + type. A wildcard 'ALL_INPUT_EVENTS' can be used if we want to match
|
| + all events.
|
| + """
|
| + if requested_type == ALL_INPUT_EVENTS:
|
| + return True
|
| +
|
| + if not 'step' in event.args:
|
| + # We don't know what type of event this is, assume its wrong
|
| + return False
|
| +
|
| + if requested_type == event.args['step']:
|
| + # These are the events we're looking for
|
| + return True
|
| +
|
| + return False
|
| +
|
| +
|
| +def GetInputEventStartAndLatency(input_events, requested_type):
|
| + """ Generate an array of events of the requested type. Each event contains
|
| + the event start time and its latency (i.e. how long it took to be
|
| + serviced).
|
| + """
|
| + start_and_latency = []
|
| + for event in input_events:
|
| +
|
| + if not EventIsOfDesireType(event, requested_type):
|
| + continue
|
| +
|
| + event_exists, start_time, end_time = FindInputEventStartAndEnd(event)
|
| +
|
| + if event_exists:
|
| +
|
| + latency = end_time - start_time
|
| + event_details = InputEventDetails(start_time, latency)
|
| + start_and_latency.append(event_details)
|
| +
|
| + return start_and_latency
|
| +
|
| +
|
| +
|
| def HasRenderingStats(process):
|
| """ Returns True if the process contains at least one
|
| BenchmarkInstrumentation::*RenderingStats event with a frame.
|
| @@ -130,6 +202,7 @@ class RenderingStats(object):
|
| # End-to-end latency for input event - from when input event is
|
| # generated to when the its resulted page is swap buffered.
|
| self.input_event_latency = []
|
| + self.input_start_and_latency_GSU = []
|
| self.frame_queueing_durations = []
|
|
|
| for timeline_range in timeline_ranges:
|
| @@ -143,6 +216,7 @@ class RenderingStats(object):
|
| self.rasterized_pixel_counts.append([])
|
| self.approximated_pixel_percentages.append([])
|
| self.input_event_latency.append([])
|
| + self.input_start_and_latency_GSU.append([])
|
|
|
| if timeline_range.is_empty:
|
| continue
|
| @@ -170,6 +244,10 @@ class RenderingStats(object):
|
| timeline_range))
|
| self.input_event_latency[-1] = ComputeInputEventLatency(latency_events)
|
|
|
| + self.input_start_and_latency_GSU[-1] = GetInputEventStartAndLatency(
|
| + latency_events,
|
| + GESTURE_SCROLL_UPDATE)
|
| +
|
| def _GatherEvents(self, event_name, process, timeline_range):
|
| events = []
|
| for event in process.IterAllSlicesOfName(event_name):
|
|
|