blob: 48029ed5adde50cda58856642ee64ae5d291484e [file] [log] [blame]
# 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.core.platform import tracing_category_filter
from telemetry.core.platform import tracing_options
from telemetry.timeline.model import TimelineModel
from telemetry.page import page_test
from telemetry.util import statistics
from telemetry.value import scalar
_CATEGORIES = ['webkit.console',
'blink.console',
'benchmark',
'toplevel',
'blink',
'cc',
'v8']
class TaskExecutionTime(page_test.PageTest):
_TIME_OUT_IN_SECONDS = 60
_NUMBER_OF_RESULTS_TO_DISPLAY = 10
def __init__(self):
super(TaskExecutionTime, self).__init__('RunSmoothness')
self._renderer_thread = None
def WillNavigateToPage(self, page, tab):
category_filter = tracing_category_filter.TracingCategoryFilter()
for category in _CATEGORIES:
category_filter.AddIncludedCategory(category)
options = tracing_options.TracingOptions()
options.enable_chrome_trace = True
tab.browser.platform.tracing_controller.Start(
options, category_filter, self._TIME_OUT_IN_SECONDS)
def DidRunActions(self, page, tab):
timeline_data = tab.browser.platform.tracing_controller.Stop()
timeline_model = TimelineModel(timeline_data=timeline_data)
self._renderer_thread = timeline_model.GetRendererThreadFromTabId(tab.id)
def ValidateAndMeasurePage(self, page, tab, results):
tasks = TaskExecutionTime.GetTasks(self._renderer_thread.parent)
sorted_tasks = sorted(tasks,
key=lambda slice: slice.median_self_duration, reverse=True)
for task in sorted_tasks[:self.GetExpectedResultCount()]:
results.AddValue(scalar.ScalarValue(
results.current_page,
task.key,
'ms',
task.median_self_duration,
description = 'Slowest tasks'))
@staticmethod
def GetTasks(process):
task_dictionary = {}
depth = 1
for parent, task_slice in enumerate(
process.IterAllSlicesOfName('MessageLoop::RunTask')):
ProcessTasksForSlice(task_dictionary, task_slice, depth, parent)
# Return dictionary flattened into a list
return task_dictionary.values()
@staticmethod
def GetExpectedResultCount():
return TaskExecutionTime._NUMBER_OF_RESULTS_TO_DISPLAY
class SliceData:
def __init__(self, key, self_duration, total_duration, depth, parent):
self.key = key
self.count = 1
self.self_durations = [self_duration]
self.min_self_duration = self_duration
self.max_self_duration = self_duration
self.total_durations = [total_duration]
self.min_total_duration = total_duration
self.max_total_duration = total_duration
self.tree_location = [(depth, parent)]
def Update(self, self_duration, total_duration, depth, parent):
self.count += 1
self.self_durations.append(self_duration)
self.min_self_duration = min(self.min_self_duration, self_duration)
self.max_self_duration = max(self.max_self_duration, self_duration)
self.total_durations.append(total_duration)
self.min_total_duration = min(self.min_total_duration, total_duration)
self.max_total_duration = max(self.max_total_duration, total_duration)
self.tree_location.append((depth, parent))
@property
def median_self_duration(self):
return statistics.Median(self.self_durations)
def ProcessTasksForSlice(dictionary, task_slice, depth, parent):
# Deal with TRACE_EVENT_INSTANTs that have no duration
self_duration = task_slice.self_thread_time or 0.0
total_duration = task_slice.thread_duration or 0.0
# There is at least one task that is tracked as both uppercase and lowercase,
# forcing the name to lowercase coalesces both.
key = task_slice.name.lower()
if key in dictionary:
dictionary[key].Update(
self_duration, total_duration, depth, parent)
else:
dictionary[key] = SliceData(
key, self_duration, total_duration, depth, parent)
# Process sub slices recursively
for sub_slice in task_slice.sub_slices:
ProcessTasksForSlice(dictionary, sub_slice, depth + 1, parent)