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) |