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 |