| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 import collections | 4 import collections |
| 5 import itertools | |
| 6 | 5 |
| 7 from metrics import Metric | 6 from metrics import Metric |
| 8 from telemetry.core.timeline.model import TimelineModel | |
| 9 from telemetry.core.timeline import bounds | 7 from telemetry.core.timeline import bounds |
| 10 from telemetry.page import page_measurement | |
| 11 | |
| 12 # All tracing categories not disabled-by-default | |
| 13 DEFAULT_TRACE_CATEGORIES = None | |
| 14 | |
| 15 # Categories for absolute minimum overhead tracing. This contains no | |
| 16 # sub-traces of thread tasks, so it's only useful for capturing the | |
| 17 # cpu-time spent on threads (as well as needed benchmark traces) | |
| 18 MINIMAL_TRACE_CATEGORIES = ("toplevel," | |
| 19 "benchmark," | |
| 20 "webkit.console," | |
| 21 "trace_event_overhead") | |
| 22 | |
| 23 class MissingFramesError(page_measurement.MeasurementFailure): | |
| 24 def __init__(self): | |
| 25 super(MissingFramesError, self).__init__( | |
| 26 'No frames found in trace. Unable to normalize results.') | |
| 27 | 8 |
| 28 | 9 |
| 29 class TimelineMetric(Metric): | 10 class TimelineMetric(Metric): |
| 30 def __init__(self): | 11 def __init__(self, model=None, renderer_process=None, action_ranges=None): |
| 31 """ Initializes a TimelineMetric object. | 12 """ Initializes a TimelineMetric object. |
| 32 """ | 13 """ |
| 33 super(TimelineMetric, self).__init__() | 14 super(TimelineMetric, self).__init__() |
| 34 self.trace_categories = DEFAULT_TRACE_CATEGORIES | |
| 35 self._model = None | |
| 36 self._renderer_process = None | |
| 37 self._actions = [] | |
| 38 self._action_ranges = [] | |
| 39 | |
| 40 def Start(self, page, tab): | |
| 41 """Starts gathering timeline data. | |
| 42 | |
| 43 """ | |
| 44 self._model = None | |
| 45 if not tab.browser.supports_tracing: | |
| 46 raise Exception('Not supported') | |
| 47 if self.trace_categories: | |
| 48 categories = [self.trace_categories] + \ | |
| 49 page.GetSyntheticDelayCategories() | |
| 50 else: | |
| 51 categories = page.GetSyntheticDelayCategories() | |
| 52 tab.browser.StartTracing(','.join(categories)) | |
| 53 | |
| 54 def Stop(self, page, tab): | |
| 55 timeline_data = tab.browser.StopTracing() | |
| 56 self._model = TimelineModel(timeline_data) | |
| 57 self._renderer_process = self._model.GetRendererProcessFromTab(tab) | |
| 58 self._action_ranges = [ action.GetActiveRangeOnTimeline(self._model) | |
| 59 for action in self._actions ] | |
| 60 # Make sure no action ranges overlap | |
| 61 for combo in itertools.combinations(self._action_ranges, 2): | |
| 62 assert not combo[0].Intersects(combo[1]) | |
| 63 | |
| 64 def AddActionToIncludeInMetric(self, action): | |
| 65 self._actions.append(action) | |
| 66 | |
| 67 @property | |
| 68 def model(self): | |
| 69 return self._model | |
| 70 | |
| 71 @model.setter | |
| 72 def model(self, model): | |
| 73 self._model = model | 15 self._model = model |
| 74 | 16 self._renderer_process = renderer_process |
| 75 @property | 17 self._action_ranges = action_ranges if action_ranges else [] |
| 76 def renderer_process(self): | |
| 77 return self._renderer_process | |
| 78 | |
| 79 @renderer_process.setter | |
| 80 def renderer_process(self, p): | |
| 81 self._renderer_process = p | |
| 82 | 18 |
| 83 def AddResults(self, tab, results): | 19 def AddResults(self, tab, results): |
| 84 return | 20 return |
| 85 | 21 |
| 86 | 22 |
| 87 class LoadTimesTimelineMetric(TimelineMetric): | 23 class LoadTimesTimelineMetric(TimelineMetric): |
| 88 def __init__(self): | 24 def __init__(self, model=None, renderer_process=None, action_ranges=None): |
| 89 super(LoadTimesTimelineMetric, self).__init__() | 25 super(LoadTimesTimelineMetric, self).__init__(model, renderer_process, |
| 26 action_ranges) |
| 90 self.report_main_thread_only = True | 27 self.report_main_thread_only = True |
| 91 | 28 |
| 92 def AddResults(self, _, results): | 29 def AddResults(self, _, results): |
| 93 assert self._model | 30 assert self._model |
| 94 if self.report_main_thread_only: | 31 if self.report_main_thread_only: |
| 95 thread_filter = 'CrRendererMain' | 32 thread_filter = 'CrRendererMain' |
| 96 else: | 33 else: |
| 97 thread_filter = None | 34 thread_filter = None |
| 98 | 35 |
| 99 events_by_name = collections.defaultdict(list) | 36 events_by_name = collections.defaultdict(list) |
| 100 | 37 |
| 101 for thread in self.renderer_process.threads.itervalues(): | 38 for thread in self._renderer_process.threads.itervalues(): |
| 102 | 39 |
| 103 if thread_filter and not thread.name in thread_filter: | 40 if thread_filter and not thread.name in thread_filter: |
| 104 continue | 41 continue |
| 105 | 42 |
| 106 thread_name = thread.name.replace('/','_') | 43 thread_name = thread.name.replace('/','_') |
| 107 events = thread.all_slices | 44 events = thread.all_slices |
| 108 | 45 |
| 109 for e in events: | 46 for e in events: |
| 110 events_by_name[e.name].append(e) | 47 events_by_name[e.name].append(e) |
| 111 | 48 |
| 112 for event_name, event_group in events_by_name.iteritems(): | 49 for event_name, event_group in events_by_name.iteritems(): |
| 113 times = [event.self_time for event in event_group] | 50 times = [event.self_time for event in event_group] |
| 114 total = sum(times) | 51 total = sum(times) |
| 115 biggest_jank = max(times) | 52 biggest_jank = max(times) |
| 116 | 53 |
| 117 # Results objects cannot contain the '.' character, so remove that here. | 54 # Results objects cannot contain the '.' character, so remove that here. |
| 118 sanitized_event_name = event_name.replace('.', '_') | 55 sanitized_event_name = event_name.replace('.', '_') |
| 119 | 56 |
| 120 full_name = thread_name + '|' + sanitized_event_name | 57 full_name = thread_name + '|' + sanitized_event_name |
| 121 results.Add(full_name, 'ms', total) | 58 results.Add(full_name, 'ms', total) |
| 122 results.Add(full_name + '_max', 'ms', biggest_jank) | 59 results.Add(full_name + '_max', 'ms', biggest_jank) |
| 123 results.Add(full_name + '_avg', 'ms', total / len(times)) | 60 results.Add(full_name + '_avg', 'ms', total / len(times)) |
| 124 | 61 |
| 125 for counter_name, counter in self.renderer_process.counters.iteritems(): | 62 for counter_name, counter in self._renderer_process.counters.iteritems(): |
| 126 total = sum(counter.totals) | 63 total = sum(counter.totals) |
| 127 | 64 |
| 128 # Results objects cannot contain the '.' character, so remove that here. | 65 # Results objects cannot contain the '.' character, so remove that here. |
| 129 sanitized_counter_name = counter_name.replace('.', '_') | 66 sanitized_counter_name = counter_name.replace('.', '_') |
| 130 | 67 |
| 131 results.Add(sanitized_counter_name, 'count', total) | 68 results.Add(sanitized_counter_name, 'count', total) |
| 132 results.Add(sanitized_counter_name + '_avg', 'count', | 69 results.Add(sanitized_counter_name + '_avg', 'count', |
| 133 total / float(len(counter.totals))) | 70 total / float(len(counter.totals))) |
| 134 | 71 |
| 135 | 72 |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 'ms', self_time_result) | 205 'ms', self_time_result) |
| 269 all_measured_time = sum(all_self_times) | 206 all_measured_time = sum(all_self_times) |
| 270 all_action_time = sum([action.bounds for action in self.action_ranges]) | 207 all_action_time = sum([action.bounds for action in self.action_ranges]) |
| 271 idle_time = max(0, all_action_time - all_measured_time) | 208 idle_time = max(0, all_action_time - all_measured_time) |
| 272 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0 | 209 idle_time_result = (float(idle_time) / num_frames) if num_frames else 0 |
| 273 results.Add(ThreadDetailResultName(self.name, "idle"), | 210 results.Add(ThreadDetailResultName(self.name, "idle"), |
| 274 'ms', idle_time_result) | 211 'ms', idle_time_result) |
| 275 | 212 |
| 276 | 213 |
| 277 class ThreadTimesTimelineMetric(TimelineMetric): | 214 class ThreadTimesTimelineMetric(TimelineMetric): |
| 278 def __init__(self): | 215 def __init__(self, model=None, renderer_process=None, action_ranges=None): |
| 279 super(ThreadTimesTimelineMetric, self).__init__() | 216 super(ThreadTimesTimelineMetric, self).__init__(model, renderer_process, |
| 217 action_ranges) |
| 280 # Minimal traces, for minimum noise in CPU-time measurements. | 218 # Minimal traces, for minimum noise in CPU-time measurements. |
| 281 self.trace_categories = MINIMAL_TRACE_CATEGORIES | |
| 282 self.results_to_report = AllThreads | 219 self.results_to_report = AllThreads |
| 283 self.details_to_report = NoThreads | 220 self.details_to_report = NoThreads |
| 284 | 221 |
| 285 def Start(self, page, tab): | |
| 286 # We need the other traces in order to have any details to report. | |
| 287 if not self.details_to_report == NoThreads: | |
| 288 self.trace_categories = DEFAULT_TRACE_CATEGORIES | |
| 289 super(ThreadTimesTimelineMetric, self).Start(page, tab) | |
| 290 | |
| 291 def CountSlices(self, slices, substring): | 222 def CountSlices(self, slices, substring): |
| 292 count = 0 | 223 count = 0 |
| 293 for event in slices: | 224 for event in slices: |
| 294 if substring in event.name: | 225 if substring in event.name: |
| 295 count += 1 | 226 count += 1 |
| 296 return count | 227 return count |
| 297 | 228 |
| 298 def AddResults(self, tab, results): | 229 def AddResults(self, tab, results): |
| 299 # We need at least one action or we won't count any slices. | 230 # We need at least one action or we won't count any slices. |
| 300 assert len(self._action_ranges) > 0 | 231 assert len(self._action_ranges) > 0 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 325 num_frames = self.CountSlices(frame_slices, FrameTraceName) | 256 num_frames = self.CountSlices(frame_slices, FrameTraceName) |
| 326 | 257 |
| 327 # Report the desired results and details. | 258 # Report the desired results and details. |
| 328 for thread_results in thread_category_results.values(): | 259 for thread_results in thread_category_results.values(): |
| 329 if thread_results.name in self.results_to_report: | 260 if thread_results.name in self.results_to_report: |
| 330 thread_results.AddResults(num_frames, results) | 261 thread_results.AddResults(num_frames, results) |
| 331 # TOOD(nduca): When generic results objects are done, this special case | 262 # TOOD(nduca): When generic results objects are done, this special case |
| 332 # can be replaced with a generic UI feature. | 263 # can be replaced with a generic UI feature. |
| 333 if thread_results.name in self.details_to_report: | 264 if thread_results.name in self.details_to_report: |
| 334 thread_results.AddDetailedResults(num_frames, results) | 265 thread_results.AddDetailedResults(num_frames, results) |
| OLD | NEW |