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): |