1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 from telemetry
.core
.platform
import tracing_category_filter
6 from telemetry
.core
.platform
import tracing_options
7 from telemetry
.timeline
.model
import TimelineModel
8 from telemetry
.page
import page_test
9 from telemetry
.util
import statistics
10 from telemetry
.value
import scalar
12 _CATEGORIES
= ['webkit.console',
21 class TaskExecutionTime(page_test
.PageTest
):
23 _TIME_OUT_IN_SECONDS
= 60
24 _NUMBER_OF_RESULTS_TO_DISPLAY
= 10
27 super(TaskExecutionTime
, self
).__init
__('RunSmoothness')
28 self
._renderer
_thread
= None
30 def WillNavigateToPage(self
, page
, tab
):
31 category_filter
= tracing_category_filter
.TracingCategoryFilter()
33 for category
in _CATEGORIES
:
34 category_filter
.AddIncludedCategory(category
)
36 options
= tracing_options
.TracingOptions()
37 options
.enable_chrome_trace
= True
39 tab
.browser
.platform
.tracing_controller
.Start(
40 options
, category_filter
, self
._TIME
_OUT
_IN
_SECONDS
)
42 def DidRunActions(self
, page
, tab
):
43 timeline_data
= tab
.browser
.platform
.tracing_controller
.Stop()
44 timeline_model
= TimelineModel(timeline_data
=timeline_data
)
45 self
._renderer
_thread
= timeline_model
.GetRendererThreadFromTabId(tab
.id)
47 def ValidateAndMeasurePage(self
, page
, tab
, results
):
48 tasks
= TaskExecutionTime
.GetTasks(self
._renderer
_thread
.parent
)
50 sorted_tasks
= sorted(tasks
,
51 key
=lambda slice: slice.median_self_duration
, reverse
=True)
53 for task
in sorted_tasks
[:self
.GetExpectedResultCount()]:
54 results
.AddValue(scalar
.ScalarValue(
58 task
.median_self_duration
,
59 description
= 'Slowest tasks'))
62 def GetTasks(process
):
65 for parent
, task_slice
in enumerate(
66 process
.IterAllSlicesOfName('MessageLoop::RunTask')):
67 ProcessTasksForSlice(task_dictionary
, task_slice
, depth
, parent
)
68 # Return dictionary flattened into a list
69 return task_dictionary
.values()
72 def GetExpectedResultCount():
73 return TaskExecutionTime
._NUMBER
_OF
_RESULTS
_TO
_DISPLAY
77 def __init__(self
, key
, self_duration
, total_duration
, depth
, parent
):
81 self
.self_durations
= [self_duration
]
82 self
.min_self_duration
= self_duration
83 self
.max_self_duration
= self_duration
85 self
.total_durations
= [total_duration
]
86 self
.min_total_duration
= total_duration
87 self
.max_total_duration
= total_duration
89 self
.tree_location
= [(depth
, parent
)]
91 def Update(self
, self_duration
, total_duration
, depth
, parent
):
94 self
.self_durations
.append(self_duration
)
95 self
.min_self_duration
= min(self
.min_self_duration
, self_duration
)
96 self
.max_self_duration
= max(self
.max_self_duration
, self_duration
)
98 self
.total_durations
.append(total_duration
)
99 self
.min_total_duration
= min(self
.min_total_duration
, total_duration
)
100 self
.max_total_duration
= max(self
.max_total_duration
, total_duration
)
102 self
.tree_location
.append((depth
, parent
))
105 def median_self_duration(self
):
106 return statistics
.Median(self
.self_durations
)
109 def ProcessTasksForSlice(dictionary
, task_slice
, depth
, parent
):
110 # Deal with TRACE_EVENT_INSTANTs that have no duration
111 self_duration
= task_slice
.self_thread_time
or 0.0
112 total_duration
= task_slice
.thread_duration
or 0.0
114 # There is at least one task that is tracked as both uppercase and lowercase,
115 # forcing the name to lowercase coalesces both.
116 key
= task_slice
.name
.lower()
117 if key
in dictionary
:
118 dictionary
[key
].Update(
119 self_duration
, total_duration
, depth
, parent
)
121 dictionary
[key
] = SliceData(
122 key
, self_duration
, total_duration
, depth
, parent
)
124 # Process sub slices recursively
125 for sub_slice
in task_slice
.sub_slices
:
126 ProcessTasksForSlice(dictionary
, sub_slice
, depth
+ 1, parent
)