| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 1 | # Copyright 2016 Google Inc. | 
|  | 2 | # | 
|  | 3 | # Use of this source code is governed by a BSD-style license that can be | 
|  | 4 | # found in the LICENSE file. | 
|  | 5 |  | 
|  | 6 | import time | 
|  | 7 |  | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 8 | class Hardware: | 
| csmartdalton | bf41fa8 | 2016-09-23 11:36:11 -0700 | [diff] [blame] | 9 | """Locks down and monitors hardware for benchmarking. | 
|  | 10 |  | 
|  | 11 | This is a common base for classes that can control the specific hardware | 
|  | 12 | we are running on. Its purpose is to lock the hardware into a constant | 
|  | 13 | benchmarking mode for the duration of a 'with' block. e.g.: | 
|  | 14 |  | 
|  | 15 | with hardware: | 
|  | 16 | run_benchmark() | 
|  | 17 |  | 
|  | 18 | While benchmarking, the caller must call sanity_check() frequently to verify | 
|  | 19 | the hardware state has not changed. | 
|  | 20 |  | 
|  | 21 | """ | 
|  | 22 |  | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 23 | def __init__(self): | 
| csmartdalton | 5772eaa | 2016-10-11 18:28:54 -0700 | [diff] [blame^] | 24 | self.warmup_time = 0 | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 25 |  | 
|  | 26 | def __enter__(self): | 
|  | 27 | return self | 
|  | 28 |  | 
|  | 29 | def __exit__(self, exception_type, exception_value, traceback): | 
|  | 30 | pass | 
|  | 31 |  | 
|  | 32 | def sanity_check(self): | 
| csmartdalton | bf41fa8 | 2016-09-23 11:36:11 -0700 | [diff] [blame] | 33 | """Raises a HardwareException if any hardware state is not as expected.""" | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 34 | pass | 
|  | 35 |  | 
| csmartdalton | 2a96101 | 2016-10-11 12:15:13 -0700 | [diff] [blame] | 36 | def print_debug_diagnostics(self): | 
|  | 37 | """Prints any info that may help improve or debug hardware monitoring.""" | 
|  | 38 | pass | 
|  | 39 |  | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 40 | def sleep(self, sleeptime): | 
| csmartdalton | bf41fa8 | 2016-09-23 11:36:11 -0700 | [diff] [blame] | 41 | """Puts the hardware into a resting state for a fixed amount of time.""" | 
| csmartdalton | d7a9db6 | 2016-09-22 05:10:02 -0700 | [diff] [blame] | 42 | time.sleep(sleeptime) | 
| csmartdalton | bf41fa8 | 2016-09-23 11:36:11 -0700 | [diff] [blame] | 43 |  | 
|  | 44 |  | 
|  | 45 | class HardwareException(Exception): | 
|  | 46 | """Gets thrown when certain hardware state is not what we expect. | 
|  | 47 |  | 
|  | 48 | Generally this happens because of thermal conditions or other variables beyond | 
|  | 49 | our control, and the appropriate course of action is to take a short nap | 
|  | 50 | before resuming the benchmark. | 
|  | 51 |  | 
|  | 52 | """ | 
|  | 53 |  | 
|  | 54 | def __init__(self, message, sleeptime=60): | 
|  | 55 | Exception.__init__(self, message) | 
|  | 56 | self.sleeptime = sleeptime | 
|  | 57 |  | 
|  | 58 |  | 
|  | 59 | class Expectation: | 
|  | 60 | """Simple helper for checking the readings on hardware gauges.""" | 
|  | 61 | def __init__(self, value_type, min_value=None, max_value=None, | 
|  | 62 | exact_value=None, name=None, sleeptime=60): | 
|  | 63 | self.value_type = value_type | 
|  | 64 | self.min_value = min_value | 
|  | 65 | self.max_value = max_value | 
|  | 66 | self.exact_value = exact_value | 
|  | 67 | self.name = name | 
|  | 68 | self.sleeptime = sleeptime | 
|  | 69 |  | 
|  | 70 | def check(self, stringvalue): | 
|  | 71 | typedvalue = self.value_type(stringvalue) | 
|  | 72 | if self.min_value is not None and typedvalue < self.min_value: | 
|  | 73 | raise HardwareException("%s is too low (%s, min=%s)" % | 
|  | 74 | (self.name, stringvalue, str(self.min_value)), | 
|  | 75 | sleeptime=self.sleeptime) | 
|  | 76 | if self.max_value is not None and typedvalue > self.max_value: | 
|  | 77 | raise HardwareException("%s is too high (%s, max=%s)" % | 
|  | 78 | (self.name, stringvalue, str(self.max_value)), | 
|  | 79 | sleeptime=self.sleeptime) | 
|  | 80 | if self.exact_value is not None and typedvalue != self.exact_value: | 
|  | 81 | raise HardwareException("unexpected %s (%s, expected=%s)" % | 
|  | 82 | (self.name, stringvalue, str(self.exact_value)), | 
|  | 83 | sleeptime=self.sleeptime) | 
|  | 84 |  | 
|  | 85 | @staticmethod | 
|  | 86 | def check_all(expectations, stringvalues): | 
|  | 87 | if len(stringvalues) != len(expectations): | 
|  | 88 | raise Exception("unexpected reading from hardware gauges " | 
|  | 89 | "(expected %i values):\n%s" % | 
|  | 90 | (len(expectations), '\n'.join(stringvalues))) | 
|  | 91 |  | 
|  | 92 | for value, expected in zip(stringvalues, expectations): | 
|  | 93 | expected.check(value) |