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',
20 class TaskExecutionTime(page_test
.PageTest
):
22 _TIME_OUT_IN_SECONDS
= 60
23 _NUMBER_OF_RESULTS_TO_DISPLAY
= 10
26 super(TaskExecutionTime
, self
).__init
__('RunSmoothness')
27 self
._renderer
_thread
= None
29 def WillNavigateToPage(self
, page
, tab
):
30 category_filter
= tracing_category_filter
.TracingCategoryFilter()
32 for category
in _CATEGORIES
:
33 category_filter
.AddIncludedCategory(category
)
35 options
= tracing_options
.TracingOptions()
36 options
.enable_chrome_trace
= True
38 tab
.browser
.platform
.tracing_controller
.Start(
39 options
, category_filter
, self
._TIME
_OUT
_IN
_SECONDS
)
41 def DidRunActions(self
, page
, tab
):
42 timeline_data
= tab
.browser
.platform
.tracing_controller
.Stop()
43 timeline_model
= TimelineModel(timeline_data
=timeline_data
)
44 self
._renderer
_thread
= timeline_model
.GetRendererThreadFromTabId(tab
.id)
46 def ValidateAndMeasurePage(self
, page
, tab
, results
):
47 tasks
= TaskExecutionTime
.GetTasks(self
._renderer
_thread
.parent
)
49 sorted_tasks
= sorted(tasks
,
50 key
=lambda slice: slice.median_self_duration
, reverse
=True)
52 for task
in sorted_tasks
[:self
.GetExpectedResultCount()]:
53 results
.AddValue(scalar
.ScalarValue(
57 task
.median_self_duration
,
58 description
= 'Slowest tasks'))
61 def GetTasks(process
):
64 for parent
, task_slice
in enumerate(
65 process
.IterAllSlicesOfName('MessageLoop::RunTask')):
66 ProcessTasksForSlice(task_dictionary
, task_slice
, depth
, parent
)
67 # Return dictionary flattened into a list
68 return task_dictionary
.values()
71 def GetExpectedResultCount():
72 return TaskExecutionTime
._NUMBER
_OF
_RESULTS
_TO
_DISPLAY
76 def __init__(self
, key
, self_duration
, total_duration
, depth
, parent
):
80 self
.self_durations
= [self_duration
]
81 self
.min_self_duration
= self_duration
82 self
.max_self_duration
= self_duration
84 self
.total_durations
= [total_duration
]
85 self
.min_total_duration
= total_duration
86 self
.max_total_duration
= total_duration
88 self
.tree_location
= [(depth
, parent
)]
90 def Update(self
, self_duration
, total_duration
, depth
, parent
):
93 self
.self_durations
.append(self_duration
)
94 self
.min_self_duration
= min(self
.min_self_duration
, self_duration
)
95 self
.max_self_duration
= max(self
.max_self_duration
, self_duration
)
97 self
.total_durations
.append(total_duration
)
98 self
.min_total_duration
= min(self
.min_total_duration
, total_duration
)
99 self
.max_total_duration
= max(self
.max_total_duration
, total_duration
)
101 self
.tree_location
.append((depth
, parent
))
104 def median_self_duration(self
):
105 return statistics
.Median(self
.self_durations
)
108 def ProcessTasksForSlice(dictionary
, task_slice
, depth
, parent
):
109 # Deal with TRACE_EVENT_INSTANTs that have no duration
110 self_duration
= task_slice
.self_thread_time
or 0.0
111 total_duration
= task_slice
.thread_duration
or 0.0
113 # There is at least one task that is tracked as both uppercase and lowercase,
114 # forcing the name to lowercase coalesces both.
115 key
= task_slice
.name
.lower()
116 if key
in dictionary
:
117 dictionary
[key
].Update(
118 self_duration
, total_duration
, depth
, parent
)
120 dictionary
[key
] = SliceData(
121 key
, self_duration
, total_duration
, depth
, parent
)
123 # Process sub slices recursively
124 for sub_slice
in task_slice
.sub_slices
:
125 ProcessTasksForSlice(dictionary
, sub_slice
, depth
+ 1, parent
)