Index: tools/telemetry/telemetry/web_perf/metrics/mainthread_jank.py |
diff --git a/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank.py b/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d26210adf09e237a8d40aa25c00550c2082d86df |
--- /dev/null |
+++ b/tools/telemetry/telemetry/web_perf/metrics/mainthread_jank.py |
@@ -0,0 +1,103 @@ |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+from telemetry.web_perf.metrics import timeline_based_metric |
+ |
+# A top slice (defined below) of a main thread can cause the webapp to behave |
+# unresponsively if its thread duration is greater than or equals to |
+# USER_PERCEIVABLE_DELAYS_THRESHOLD_MS. Human eyes can perceive delay at low as |
+# 100ms, but since we use thread time instead of wall-time, we reduce the |
+# threshold further to 50ms to make room for other OS's activity. |
+USER_PERCEIVABLE_DELAYS_THRESHOLD_MS = 50 |
chrishenry
2014/05/19 23:16:50
s/DELAYS/DELAY/
|
+ |
+ |
+def IsTopSlice(event): |
+ """Whether event refers to a top MessageLoop task, i.e: there is no |
+ other MessageLoop task on top of this task and contains it. |
+ |
+ Returns: |
+ True if this timeline event refers to the top MesssageLoop task. |
+ False otherwise. |
+ """ |
+ return event.name == 'MessageLoop::RunTask' and event.category == 'toplevel' |
+ |
+ |
+class _MainthreadJankStat(object): |
+ """A small wrapper class for storing stats related to mainthread jank.""" |
+ |
+ def __init__(self): |
+ self.sum_top_slices_thread_time = 0 |
+ self.num_big_top_slices = 0 |
+ self.sum_big_top_slices_thread_time = 0 |
+ self.biggest_top_slice_thread_time = 0 |
+ |
+ |
+def _ComputeMainthreadJankStats(renderer_thread, thread_start, thread_end): |
+ """Computes the mainthread jank stat on the range (thread_start, thread_end). |
+ |
+ Returns: |
+ An instance of _MainthreadJankStat, which has: |
+ |
+ sum_top_slices_thread_time is the total thread duration of all top slices |
+ whose thread time ranges overlapped with (thread_start, thread_end). |
+ |
+ num_big_top_slices is the number of top slices whose thread time ranges |
+ overlapped with (thread_start, thread_end) and the overlapped thread |
+ duration is greater than or equal USER_PERCEIVABLE_DELAYS_THRESHOLD_MS. |
+ |
+ sum_big_top_slices_thread_time is the total thread duration of all top |
+ slices whose thread time ranges overlapped with (thread_start, thread_end) |
+ and the overlapped thread duration is greater than or equal |
+ USER_PERCEIVABLE_DELAYS_THRESHOLD_MS. |
+ |
+ biggest_top_slice_thread_time is the biggest thread duration of all |
+ top slices whose thread time ranges overlapped with |
+ (thread_start, thread_end). |
+ |
+ Note: thread duration of each slices is computed using overlapped range |
+ with (thread_start, thread_end). |
+ """ |
+ stat = _MainthreadJankStat() |
+ for s in renderer_thread.IterAllSlicesOverlappedWithThreadTimeRange( |
+ thread_start, thread_end): |
+ if not IsTopSlice(s): |
+ continue |
+ jank_thread_duration = s.GetOverlappedThreadTimeDurationWithRange( |
+ thread_start, thread_end) |
+ stat.sum_top_slices_thread_time += jank_thread_duration |
+ stat.biggest_top_slice_thread_time = max( |
+ stat.biggest_top_slice_thread_time, jank_thread_duration) |
+ if jank_thread_duration >= USER_PERCEIVABLE_DELAYS_THRESHOLD_MS: |
+ stat.num_big_top_slices += 1 |
+ stat.sum_big_top_slices_thread_time += jank_thread_duration |
+ return stat |
+ |
+ |
+class MainthreadJankMetric(timeline_based_metric.TimelineBasedMetric): |
+ |
+ def __init__(self): |
+ super(MainthreadJankMetric, self).__init__() |
+ |
+ def AddResults(self, _, renderer_thread, interaction_records, results): |
+ self.VerifyNonOverlappedRecords(interaction_records) |
+ num_big_janks = 0 |
+ total_jank_thread_time = 0 |
+ total_big_jank_thread_time = 0 |
+ biggest_jank_thread_time = 0 |
+ |
+ for record in interaction_records: |
+ record_jank_stat = _ComputeMainthreadJankStats( |
+ renderer_thread, record.thread_start, record.thread_end) |
+ num_big_janks += record_jank_stat.num_big_top_slices |
+ total_jank_thread_time += record_jank_stat.sum_top_slices_thread_time |
+ total_big_jank_thread_time += ( |
+ record_jank_stat.sum_big_top_slices_thread_time) |
+ biggest_jank_thread_time = ( |
+ max(biggest_jank_thread_time, |
+ record_jank_stat.biggest_top_slice_thread_time)) |
+ |
+ results.Add('num_big_janks', 'score', num_big_janks) |
+ results.Add('total_jank_thread_time', 'ms', total_jank_thread_time) |
+ results.Add('total_big_jank_thread_time', 'ms', total_big_jank_thread_time) |
+ results.Add('biggest_jank_thread_time', 'ms', biggest_jank_thread_time) |