| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """This module contains PerformanceLogProcessor and subclasses. | 5 """This module contains PerformanceLogProcessor and subclasses. |
| 6 | 6 |
| 7 Several performance tests have complicated log output, this module is intended | 7 Several performance tests have complicated log output, this module is intended |
| 8 to help buildsteps parse these logs and identify if tests had anomalies. | 8 to help buildsteps parse these logs and identify if tests had anomalies. |
| 9 | 9 |
| 10 The classes in this file all have the same method ProcessLine, just like | 10 The classes in this file all have the same method ProcessLine, just like |
| 11 GTestLogParser in //tools/build/scripts/common/gtest_utils.py. They also | 11 GTestLogParser in //tools/build/scripts/common/gtest_utils.py. They also |
| 12 construct a set of files which are used for graphing. | 12 construct a set of files which are used for graphing. |
| 13 | 13 |
| 14 Note: This module is doomed to be deprecated in the future, as Telemetry | 14 Note: This module is doomed to be deprecated in the future, as Telemetry |
| 15 results will be passed more directly to the new performance dashboard. | 15 results will be passed more directly to the new performance dashboard. |
| 16 """ | 16 """ |
| 17 | 17 |
| 18 import collections | 18 import collections |
| 19 import json | 19 import json |
| 20 import logging | 20 import logging |
| 21 import os | 21 import os |
| 22 import re | 22 import re |
| 23 | 23 |
| 24 from common import chromium_utils | 24 from common import chromium_utils |
| 25 |
| 26 # TODO(crbug.com/403564). |
| 25 import config | 27 import config |
| 26 | 28 |
| 27 # Status codes that can be returned by the evaluateCommand method. | 29 # Status codes that can be returned by the evaluateCommand method. |
| 28 # From buildbot.status.builder. | 30 # From buildbot.status.builder. |
| 29 # See: http://docs.buildbot.net/current/developer/results.html | 31 # See: http://docs.buildbot.net/current/developer/results.html |
| 30 SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY = range(6) | 32 SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY = range(6) |
| 31 | 33 |
| 32 | 34 |
| 33 def _FormatFloat(number): | |
| 34 """Formats float with two decimal points.""" | |
| 35 if number: | |
| 36 return '%.2f' % number | |
| 37 else: | |
| 38 return '0.00' | |
| 39 | |
| 40 | |
| 41 def _FormatPercentage(ratio): | |
| 42 """Formats a number as a string with a percentage (e.g. 0.5 => "50%").""" | |
| 43 return '%s%%' % _FormatFloat(100 * ratio) | |
| 44 | |
| 45 | |
| 46 def _Divide(x, y): | |
| 47 """Divides with float division, or returns infinity if denominator is 0.""" | |
| 48 if y == 0: | |
| 49 return float('inf') | |
| 50 return float(x) / y | |
| 51 | |
| 52 | |
| 53 def _FormatHumanReadable(number): | |
| 54 """Formats a float into three significant figures, using metric suffixes. | |
| 55 | |
| 56 Only m, k, and M prefixes (for 1/1000, 1000, and 1,000,000) are used. | |
| 57 Examples: | |
| 58 0.0387 => 38.7m | |
| 59 1.1234 => 1.12 | |
| 60 10866 => 10.8k | |
| 61 682851200 => 683M | |
| 62 """ | |
| 63 metric_prefixes = {-3: 'm', 0: '', 3: 'k', 6: 'M'} | |
| 64 scientific = '%.2e' % float(number) # 6.83e+005 | |
| 65 e_idx = scientific.find('e') # 4, or 5 if negative | |
| 66 digits = float(scientific[:e_idx]) # 6.83 | |
| 67 exponent = int(scientific[e_idx + 1:]) # int('+005') = 5 | |
| 68 while exponent % 3: | |
| 69 digits *= 10 | |
| 70 exponent -= 1 | |
| 71 while exponent > 6: | |
| 72 digits *= 10 | |
| 73 exponent -= 1 | |
| 74 while exponent < -3: | |
| 75 digits /= 10 | |
| 76 exponent += 1 | |
| 77 if digits >= 100: | |
| 78 # Don't append a meaningless '.0' to an integer number. | |
| 79 digits = int(digits) | |
| 80 # Exponent is now divisible by 3, between -3 and 6 inclusive: (-3, 0, 3, 6). | |
| 81 return '%s%s' % (digits, metric_prefixes[exponent]) | |
| 82 | |
| 83 | |
| 84 def _JoinWithSpacesAndNewLine(words): | |
| 85 """Joins a list of words together with spaces.""" | |
| 86 return ' '.join(str(w) for w in words) + '\n' | |
| 87 | |
| 88 | |
| 89 class PerformanceLogProcessor(object): | 35 class PerformanceLogProcessor(object): |
| 90 """Parent class for performance log parsers. | 36 """Parent class for performance log parsers. |
| 91 | 37 |
| 92 The only essential public method that subclasses must define is the method | 38 The only essential public method that subclasses must define is the method |
| 93 ProcessLine, which takes one line of a test output log and uses it | 39 ProcessLine, which takes one line of a test output log and uses it |
| 94 to change the internal state of the PerformanceLogProcessor object, | 40 to change the internal state of the PerformanceLogProcessor object, |
| 95 so that methods such as PerformanceLogs return the right thing. | 41 so that methods such as PerformanceLogs return the right thing. |
| 96 """ | 42 """ |
| 97 | 43 |
| 98 # The file perf_expectations.json holds performance expectations. | 44 # The file perf_expectations.json holds performance expectations. |
| (...skipping 734 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 for page in self._page_list: | 779 for page in self._page_list: |
| 834 times = page_times[page] | 780 times = page_times[page] |
| 835 mean, stddev = chromium_utils.FilteredMeanAndStandardDeviation(times) | 781 mean, stddev = chromium_utils.FilteredMeanAndStandardDeviation(times) |
| 836 file_data.append('%s (%s+/-%s): %s' % (page, | 782 file_data.append('%s (%s+/-%s): %s' % (page, |
| 837 _FormatFloat(mean), | 783 _FormatFloat(mean), |
| 838 _FormatFloat(stddev), | 784 _FormatFloat(stddev), |
| 839 _JoinWithSpacesAndNewLine(times))) | 785 _JoinWithSpacesAndNewLine(times))) |
| 840 | 786 |
| 841 filename = '%s_%s.dat' % (self._revision, trace_name) | 787 filename = '%s_%s.dat' % (self._revision, trace_name) |
| 842 return {filename: file_data} | 788 return {filename: file_data} |
| 789 |
| 790 |
| 791 def _FormatFloat(number): |
| 792 """Formats float with two decimal points.""" |
| 793 if number: |
| 794 return '%.2f' % number |
| 795 else: |
| 796 return '0.00' |
| 797 |
| 798 |
| 799 def _FormatPercentage(ratio): |
| 800 """Formats a number as a string with a percentage (e.g. 0.5 => "50%").""" |
| 801 return '%s%%' % _FormatFloat(100 * ratio) |
| 802 |
| 803 |
| 804 def _Divide(x, y): |
| 805 """Divides with float division, or returns infinity if denominator is 0.""" |
| 806 if y == 0: |
| 807 return float('inf') |
| 808 return float(x) / y |
| 809 |
| 810 |
| 811 def _FormatHumanReadable(number): |
| 812 """Formats a float into three significant figures, using metric suffixes. |
| 813 |
| 814 Only m, k, and M prefixes (for 1/1000, 1000, and 1,000,000) are used. |
| 815 Examples: |
| 816 0.0387 => 38.7m |
| 817 1.1234 => 1.12 |
| 818 10866 => 10.8k |
| 819 682851200 => 683M |
| 820 """ |
| 821 metric_prefixes = {-3: 'm', 0: '', 3: 'k', 6: 'M'} |
| 822 scientific = '%.2e' % float(number) # 6.83e+005 |
| 823 e_idx = scientific.find('e') # 4, or 5 if negative |
| 824 digits = float(scientific[:e_idx]) # 6.83 |
| 825 exponent = int(scientific[e_idx + 1:]) # int('+005') = 5 |
| 826 while exponent % 3: |
| 827 digits *= 10 |
| 828 exponent -= 1 |
| 829 while exponent > 6: |
| 830 digits *= 10 |
| 831 exponent -= 1 |
| 832 while exponent < -3: |
| 833 digits /= 10 |
| 834 exponent += 1 |
| 835 if digits >= 100: |
| 836 # Don't append a meaningless '.0' to an integer number. |
| 837 digits = int(digits) |
| 838 # Exponent is now divisible by 3, between -3 and 6 inclusive: (-3, 0, 3, 6). |
| 839 return '%s%s' % (digits, metric_prefixes[exponent]) |
| 840 |
| 841 |
| 842 def _JoinWithSpacesAndNewLine(words): |
| 843 """Joins a list of words together with spaces.""" |
| 844 return ' '.join(str(w) for w in words) + '\n' |
| OLD | NEW |