| Index: tools/perf/measurements/task_execution_time.py
|
| diff --git a/tools/perf/measurements/task_execution_time.py b/tools/perf/measurements/task_execution_time.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e4e2be031e0fce2ff2e28797e013554dd38bcc51
|
| --- /dev/null
|
| +++ b/tools/perf/measurements/task_execution_time.py
|
| @@ -0,0 +1,125 @@
|
| +# 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__('RunTaskExecutionTime')
|
| + 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)
|
|
|