Home | History | Annotate | Download | only in cros
      1 class Metric(object):
      2     """Abstract base class for metrics."""
      3     def __init__(self,
      4                  description,
      5                  units=None,
      6                  higher_is_better=False):
      7         """
      8         Initializes a Metric.
      9         @param description: Description of the metric, e.g., used as label on a
     10                 dashboard chart
     11         @param units: Units of the metric, e.g. percent, seconds, MB.
     12         @param higher_is_better: Whether a higher value is considered better or
     13                 not.
     14         """
     15         self._description = description
     16         self._units = units
     17         self._higher_is_better = higher_is_better
     18         self._samples = []
     19 
     20     @property
     21     def description(self):
     22         """Description of the metric."""
     23         return self._description
     24 
     25     @property
     26     def units(self):
     27         """Units of the metric."""
     28         return self._units
     29 
     30     @property
     31     def higher_is_better(self):
     32         """Whether a higher value is considered better or not."""
     33         return self._higher_is_better
     34 
     35     @property
     36     def values(self):
     37         """Measured values of the metric."""
     38         if len(self._samples) == 0:
     39             return self._samples
     40         return self._aggregate(self._samples)
     41 
     42     @values.setter
     43     def values(self, samples):
     44         self._samples = samples
     45 
     46     def _aggregate(self, samples):
     47         """
     48         Subclasses can override this to aggregate the metric into a single
     49         sample.
     50         """
     51         return samples
     52 
     53     def pre_collect(self):
     54         """
     55         Hook called before metrics are being collected.
     56         """
     57         pass
     58 
     59     def _store_sample(self, sample):
     60         self._samples.append(sample)
     61 
     62     def collect_metric(self):
     63         """
     64         Collects one sample.
     65 
     66         Implementations should call self._store_sample() once if it's not an
     67         aggregate, i.e., it overrides self._aggregate().
     68         """
     69         pass
     70 
     71     @classmethod
     72     def from_metric(cls, other):
     73         """
     74         Instantiate from an existing metric instance.
     75         """
     76         metric = cls(
     77                 description=other.description,
     78                 units=other.units,
     79                 higher_is_better=other.higher_is_better)
     80         metric.values = other.values
     81         return metric
     82 
     83 class PeakMetric(Metric):
     84     """
     85     Metric that collects the peak of another metric.
     86     """
     87 
     88     @property
     89     def description(self):
     90         return 'peak_' + super(PeakMetric, self).description
     91 
     92     def _aggregate(self, samples):
     93         return max(samples)
     94 
     95 class SumMetric(Metric):
     96     """
     97     Metric that sums another metric.
     98     """
     99 
    100     @property
    101     def description(self):
    102         return 'sum_' + super(SumMetric, self).description
    103 
    104     def _aggregate(self, samples):
    105         return sum(samples)
    106 
    107 class MemUsageMetric(Metric):
    108     """
    109     Metric that collects memory usage in percent.
    110 
    111     Memory usage is collected in percent. Buffers and cached are calculated
    112     as free memory.
    113     """
    114     def __init__(self, system_facade):
    115         super(MemUsageMetric, self).__init__('memory_usage', units='percent')
    116         self.system_facade = system_facade
    117 
    118     def collect_metric(self):
    119         total_memory = self.system_facade.get_mem_total()
    120         free_memory = self.system_facade.get_mem_free_plus_buffers_and_cached()
    121         used_memory = total_memory - free_memory
    122         usage_percent = (used_memory * 100) / total_memory
    123         self._store_sample(usage_percent)
    124 
    125 class CpuUsageMetric(Metric):
    126     """
    127     Metric that collects cpu usage in percent.
    128     """
    129     def __init__(self, system_facade):
    130         super(CpuUsageMetric, self).__init__('cpu_usage', units='percent')
    131         self.last_usage = None
    132         self.system_facade = system_facade
    133 
    134     def pre_collect(self):
    135         self.last_usage = self.system_facade.get_cpu_usage()
    136 
    137     def collect_metric(self):
    138         """
    139         Collects CPU usage in percent.
    140         """
    141         current_usage = self.system_facade.get_cpu_usage()
    142         # Compute the percent of active time since the last update to
    143         # current_usage.
    144         usage_percent = 100 * self.system_facade.compute_active_cpu_time(
    145                 self.last_usage, current_usage)
    146         self._store_sample(usage_percent)
    147         self.last_usage = current_usage
    148 
    149 class AllocatedFileHandlesMetric(Metric):
    150     """
    151     Metric that collects the number of allocated file handles.
    152     """
    153     def __init__(self, system_facade):
    154         super(AllocatedFileHandlesMetric, self).__init__(
    155                 'allocated_file_handles', units='handles')
    156         self.system_facade = system_facade
    157 
    158     def collect_metric(self):
    159         self._store_sample(self.system_facade.get_num_allocated_file_handles())
    160 
    161 class StorageWrittenMetric(Metric):
    162     """
    163     Metric that collects amount of data written to persistent storage.
    164     """
    165     def __init__(self, system_facade):
    166         super(StorageWrittenMetric, self).__init__(
    167                 'storage_written', units='kB')
    168         self.last_written_kb = None
    169         self.system_facade = system_facade
    170 
    171     def pre_collect(self):
    172         statistics = self.system_facade.get_storage_statistics()
    173         self.last_written_kb = statistics['written_kb']
    174 
    175     def collect_metric(self):
    176         """
    177         Collects total amount of data written to persistent storage in kB.
    178         """
    179         statistics = self.system_facade.get_storage_statistics()
    180         written_kb = statistics['written_kb']
    181         written_period = written_kb - self.last_written_kb
    182         self._store_sample(written_period)
    183         self.last_written_kb = written_kb
    184 
    185 class TemperatureMetric(Metric):
    186     """
    187     Metric that collects the max of the temperatures measured on all sensors.
    188     """
    189     def __init__(self, system_facade):
    190         super(TemperatureMetric, self).__init__('temperature', units='Celsius')
    191         self.system_facade = system_facade
    192 
    193     def collect_metric(self):
    194         self._store_sample(self.system_facade.get_current_temperature_max())
    195 
    196 def create_default_metric_set(system_facade):
    197     """
    198     Creates the default set of metrics.
    199 
    200     @param system_facade the system facade to initialize the metrics with.
    201     @return a list with Metric instances.
    202     """
    203     cpu = CpuUsageMetric(system_facade)
    204     mem = MemUsageMetric(system_facade)
    205     file_handles = AllocatedFileHandlesMetric(system_facade)
    206     storage_written = StorageWrittenMetric(system_facade)
    207     temperature = TemperatureMetric(system_facade)
    208     peak_cpu = PeakMetric.from_metric(cpu)
    209     peak_mem = PeakMetric.from_metric(mem)
    210     peak_temperature = PeakMetric.from_metric(temperature)
    211     sum_storage_written = SumMetric.from_metric(storage_written)
    212     return [cpu,
    213             mem,
    214             file_handles,
    215             storage_written,
    216             temperature,
    217             peak_cpu,
    218             peak_mem,
    219             peak_temperature,
    220             sum_storage_written]
    221 
    222 class SystemMetricsCollector(object):
    223     """
    224     Collects system metrics.
    225     """
    226     def __init__(self, system_facade, metrics = None):
    227         """
    228         Initialize with facade and metric classes.
    229 
    230         @param system_facade The system facade to use for querying the system,
    231                 e.g. system_facade_native.SystemFacadeNative for client tests.
    232         @param metrics List of metric instances. If None, the default set will
    233                 be created.
    234         """
    235         self.metrics = (create_default_metric_set(system_facade)
    236                         if metrics is None else metrics)
    237 
    238     def pre_collect(self):
    239         """
    240         Calls pre hook of metrics.
    241         """
    242         for metric in self.metrics:
    243             metric.pre_collect()
    244 
    245     def collect_snapshot(self):
    246         """
    247         Collects one snapshot of metrics.
    248         """
    249         for metric in self.metrics:
    250             metric.collect_metric()
    251 
    252     def write_metrics(self, writer_function):
    253         """
    254         Writes the collected metrics using the specified writer function.
    255 
    256         @param writer_function: A function with the following signature:
    257                  f(description, value, units, higher_is_better)
    258         """
    259         for metric in self.metrics:
    260             writer_function(
    261                     description=metric.description,
    262                     value=metric.values,
    263                     units=metric.units,
    264                     higher_is_better=metric.higher_is_better)
    265