OLD | NEW |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2013 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 | 4 |
5 from collections import defaultdict | 5 from collections import defaultdict |
6 from itertools import chain | |
7 | 6 |
| 7 from telemetry import value as value_module |
8 from telemetry.page import page_measurement_results | 8 from telemetry.page import page_measurement_results |
9 from telemetry.page import perf_tests_helper | 9 from telemetry.page import perf_tests_helper |
| 10 from telemetry.value import merge_values |
| 11 |
10 | 12 |
11 class BuildbotPageMeasurementResults( | 13 class BuildbotPageMeasurementResults( |
12 page_measurement_results.PageMeasurementResults): | 14 page_measurement_results.PageMeasurementResults): |
13 def __init__(self, trace_tag=''): | 15 def __init__(self, trace_tag=''): |
14 super(BuildbotPageMeasurementResults, self).__init__() | 16 super(BuildbotPageMeasurementResults, self).__init__() |
15 self._trace_tag = trace_tag | 17 self._trace_tag = trace_tag |
16 | 18 |
17 def _PrintPerfResult(self, measurement, trace, values, units, | 19 def _PrintPerfResult(self, measurement, trace, v, units, |
18 result_type='default'): | 20 result_type='default'): |
19 perf_tests_helper.PrintPerfResult( | 21 perf_tests_helper.PrintPerfResult( |
20 measurement, trace, values, units, result_type) | 22 measurement, trace, v, units, result_type) |
21 | 23 |
22 def PrintSummary(self): | 24 def PrintSummary(self): |
23 """Print summary data in a format expected by buildbot for perf dashboards. | 25 """Print summary data in a format expected by buildbot for perf dashboards. |
24 | 26 |
25 If any failed pages exist, only output individual page results for | 27 If any failed pages exist, only output individual page results for |
26 non-failing pages, and do not output any average data. | 28 non-failing pages, and do not output any average data. |
27 """ | 29 """ |
28 if self.errors or self.failures: | 30 # Print out the list of unique pages. |
29 success_page_results = [r for r in self._page_results | 31 perf_tests_helper.PrintPages( |
30 if r.page.url not in | 32 [page.display_name for page in self.pages_that_succeeded]) |
31 zip(*self.errors + self.failures)[0]] | 33 self._PrintPerPageResults() |
32 else: | 34 self._PrintOverallResults() |
33 success_page_results = self._page_results | |
34 | 35 |
35 # Print out the list of unique pages. | 36 @property |
36 # Use a set and a list to efficiently create an order preserving list of | 37 def had_errors_or_failures(self): |
37 # unique page display_names. | 38 return self.errors or self.failures |
38 unique_pages = [] | |
39 unique_pages_set = set() | |
40 for page_values in success_page_results: | |
41 name = page_values.page.display_name | |
42 if name in unique_pages_set: | |
43 continue | |
44 unique_pages.append(name) | |
45 unique_pages_set.add(name) | |
46 perf_tests_helper.PrintPages(unique_pages) | |
47 | 39 |
48 # Build the results summary. | 40 def _PrintPerPageResults(self): |
49 results_summary = defaultdict(list) | 41 all_successful_page_values = ( |
50 for measurement_name in \ | 42 self.GetAllPageSpecificValuesForSuccessfulPages()) |
51 self._all_measurements_that_have_been_seen.iterkeys(): | |
52 for page_values in success_page_results: | |
53 value = page_values.FindValueByMeasurementName(measurement_name) | |
54 if not value: | |
55 continue | |
56 measurement_units_type = (measurement_name, | |
57 value.units, | |
58 value.data_type) | |
59 value_and_display_name = (value.value, page_values.page.display_name) | |
60 results_summary[measurement_units_type].append(value_and_display_name) | |
61 | 43 |
62 # Output the results summary sorted by measurement name, then units, then | 44 had_exactly_one_successful_page = len( |
63 # data type. | 45 set([v.page for v in all_successful_page_values])) == 1 |
64 for measurement_units_type, value_and_display_name_list in sorted( | |
65 results_summary.iteritems()): | |
66 measurement, units, data_type = measurement_units_type | |
67 | 46 |
68 if 'histogram' in data_type: | 47 # By here, due to page repeat options, all_values_from_successful_pages |
69 by_name_data_type = 'unimportant-histogram' | 48 # contains values of the same name not only from mulitple pages, but also |
| 49 # from the same name. So even if, for instance, only one page ran, it may |
| 50 # have run twice, producing two 'x' values. |
| 51 # |
| 52 # So, get rid of the repeated pages by merging. |
| 53 merged_page_values = merge_values.MergeLikeValuesFromSamePage( |
| 54 all_successful_page_values) |
| 55 |
| 56 # Now we have a bunch of values, but there is only one value_name per page. |
| 57 # Suppose page1 and page2 ran, producing values x and y. We want to print |
| 58 # x_by_url for page1 |
| 59 # x_by_url for page2 |
| 60 # x for page1, page2 combined |
| 61 # |
| 62 # y_by_url for page1 |
| 63 # y_by_url for page2 |
| 64 # y for page1, page2 combined |
| 65 # |
| 66 # We already have the x_by_url values in the values array. But, we will need |
| 67 # them indexable by the value name. |
| 68 # |
| 69 # The following dict maps value_name -> list of pages that have values of |
| 70 # that name. |
| 71 per_page_values_by_value_name = defaultdict(list) |
| 72 for value in merged_page_values: |
| 73 per_page_values_by_value_name[value.name].append(value) |
| 74 |
| 75 # We already have the x_by_url values in the values array. But, we also need |
| 76 # the values merged across the pages. And, we will need them indexed by |
| 77 # value name so that we can find them when printing out value names in |
| 78 # alphabetical order. |
| 79 merged_pages_value_by_value_name = {} |
| 80 for value in merge_values.MergeLikeValuesFromDifferentPages( |
| 81 all_successful_page_values): |
| 82 assert value.name not in merged_pages_value_by_value_name |
| 83 merged_pages_value_by_value_name[value.name] = value |
| 84 |
| 85 # sorted_value names will govern the order we start printing values. |
| 86 value_names = set([v.name for v in merged_page_values]) |
| 87 sorted_value_names = sorted(value_names) |
| 88 |
| 89 # Time to walk through the values by name, printing first the by_url values |
| 90 # and then the merged_site value. |
| 91 for value_name in sorted_value_names: |
| 92 per_page_values = per_page_values_by_value_name.get(value_name, []) |
| 93 |
| 94 # Sort the values by their url |
| 95 sorted_per_page_values = list(per_page_values) |
| 96 sorted_per_page_values.sort( |
| 97 key=lambda per_page_values: per_page_values.page.display_name) |
| 98 |
| 99 # Output the _by_url results. |
| 100 for per_page_value in sorted_per_page_values: |
| 101 self._PrintPerPageValue(per_page_value, had_exactly_one_successful_page) |
| 102 |
| 103 # Output the combined values. |
| 104 merged_pages_value = merged_pages_value_by_value_name.get(value_name, |
| 105 None) |
| 106 if merged_pages_value: |
| 107 self._PrintMergedPagesValue(merged_pages_value) |
| 108 |
| 109 def _PrintPerPageValue(self, value, had_exactly_one_successful_page): |
| 110 # We dont print per-page-values when there is a trace tag. |
| 111 if self._trace_tag: |
| 112 return |
| 113 |
| 114 # If there were any page errors, we typically will print nothing. |
| 115 # |
| 116 # Note: this branch is structured less-densely to improve legibility. |
| 117 if self.had_errors_or_failures: |
| 118 if had_exactly_one_successful_page: |
| 119 pass |
70 else: | 120 else: |
71 by_name_data_type = 'unimportant' | 121 return |
72 if '.' in measurement and 'histogram' not in data_type: | |
73 measurement, trace = measurement.split('.', 1) | |
74 trace += self._trace_tag | |
75 else: | |
76 trace = measurement + self._trace_tag | |
77 | 122 |
78 # Print individual _by_name results if there's more than 1 successful | 123 # Actually print the result. |
79 # page, or if there's exactly 1 successful page but a failure exists. | 124 buildbot_value = value.GetBuildbotValue() |
80 if not self._trace_tag and (len(value_and_display_name_list) > 1 or | 125 buildbot_data_type = value.GetBuildbotDataType( |
81 ((self.errors or self.failures) and | 126 output_context=value_module.PER_PAGE_RESULT_OUTPUT_CONTEXT) |
82 len(value_and_display_name_list) == 1)): | 127 buildbot_measurement_name, buildbot_trace_name = ( |
83 name_value_map = defaultdict(list) | 128 value.GetBuildbotMeasurementAndTraceNameForPerPageResult()) |
84 for value, name in value_and_display_name_list: | 129 self._PrintPerfResult(buildbot_measurement_name, |
85 if 'histogram' in data_type and name_value_map[name]: | 130 buildbot_trace_name, |
86 # TODO(tonyg/marja): The histogram processing code only accepts one | 131 buildbot_value, value.units, buildbot_data_type) |
87 # histogram, so we only report the first histogram. Once histograms | |
88 # support aggregating multiple values, this can be removed. | |
89 continue | |
90 name_value_map[name].append(value) | |
91 for name in unique_pages: | |
92 values = perf_tests_helper.FlattenList(name_value_map[name]) | |
93 if not len(values): | |
94 continue | |
95 self._PrintPerfResult(measurement + '_by_url', name, | |
96 values, units, by_name_data_type) | |
97 | 132 |
98 # If there were no page failures, print the average data. | 133 def _PrintMergedPagesValue(self, value): |
99 # For histograms, we don't print the average data, only the _by_name, | 134 # If there were any page errors, we typically will print nothing. |
100 # unless there is only 1 page in which case the _by_names are omitted. | 135 # |
101 if not (self.errors or self.failures): | 136 # Note: this branch is structured less-densely to improve legibility. |
102 if ('histogram' not in data_type or | 137 if self.had_errors_or_failures: |
103 len(value_and_display_name_list) == 1): | 138 return |
104 values = [i[0] for i in value_and_display_name_list] | |
105 if isinstance(values[0], list): | |
106 values = list(chain.from_iterable(values)) | |
107 self._PrintPerfResult(measurement, trace, values, units, data_type) | |
108 | 139 |
| 140 buildbot_value = value.GetBuildbotValue() |
| 141 buildbot_data_type = value.GetBuildbotDataType( |
| 142 output_context=value_module.MERGED_PAGES_RESULT_OUTPUT_CONTEXT) |
| 143 buildbot_measurement_name, buildbot_trace_name = ( |
| 144 value.GetBuildbotMeasurementAndTraceNameForMergedPagesResult( |
| 145 self._trace_tag)) |
| 146 |
| 147 self._PrintPerfResult(buildbot_measurement_name, |
| 148 buildbot_trace_name, |
| 149 buildbot_value, value.units, buildbot_data_type) |
| 150 |
| 151 def _PrintOverallResults(self): |
109 # If there were no failed pages, output the overall results (results not | 152 # If there were no failed pages, output the overall results (results not |
110 # associated with a page). | 153 # associated with a page). |
111 if not (self.errors or self.failures): | 154 if not self.had_errors_or_failures: |
112 for value in self._overall_results: | 155 for value in self._all_summary_values: |
113 values = value.value | 156 buildbot_value = value.GetBuildbotValue() |
114 if not isinstance(values, list): | 157 buildbot_data_type = value.GetBuildbotDataType( |
115 values = [values] | 158 output_context=value_module.SUMMARY_RESULT_OUTPUT_CONTEXT) |
116 measurement_name = value.chart_name | 159 buildbot_measurement_name, buildbot_trace_name = ( |
117 if not measurement_name: | 160 value.GetBuildbotMeasurementAndTraceNameForMergedPagesResult( |
118 measurement_name = value.trace_name | 161 self._trace_tag)) |
119 self._PrintPerfResult(measurement_name, | 162 self._PrintPerfResult( |
120 value.trace_name + self._trace_tag, | 163 buildbot_measurement_name, |
121 values, value.units, value.data_type) | 164 buildbot_trace_name, |
| 165 buildbot_value, |
| 166 value.units, |
| 167 buildbot_data_type) |
| 168 |
122 | 169 |
123 # Print the number of failed and errored pages. | 170 # Print the number of failed and errored pages. |
124 self._PrintPerfResult('telemetry_page_measurement_results', 'num_failed', | 171 self._PrintPerfResult('telemetry_page_measurement_results', 'num_failed', |
125 [len(self.failures)], 'count', 'unimportant') | 172 [len(self.failures)], 'count', 'unimportant') |
126 self._PrintPerfResult('telemetry_page_measurement_results', 'num_errored', | 173 self._PrintPerfResult('telemetry_page_measurement_results', 'num_errored', |
127 [len(self.errors)], 'count', 'unimportant') | 174 [len(self.errors)], 'count', 'unimportant') |
128 | 175 |
129 super(BuildbotPageMeasurementResults, self).PrintSummary() | 176 super(BuildbotPageMeasurementResults, self).PrintSummary() |
OLD | NEW |