1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# found in the LICENSE file. 4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import collections 6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import logging 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import os 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import plistlib 9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import shutil 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import tempfile 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import xml.parsers.expat 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from telemetry import decorators 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from telemetry.core import util 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from telemetry.core.platform import platform_backend 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from telemetry.core.platform import power_monitor 17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class PowerMetricsPowerMonitor(power_monitor.PowerMonitor): 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def __init__(self, backend): 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) super(PowerMetricsPowerMonitor, self).__init__() 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._powermetrics_process = None 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._backend = backend 24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._output_filename = None 25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._output_directory = None 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @property 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def binary_path(self): 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return '/usr/bin/powermetrics' 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 31c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def StartMonitoringPower(self, browser): 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert not self._powermetrics_process, ( 33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch "Must call StopMonitoringPower().") 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Empirically powermetrics creates an empty output file immediately upon 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # starting. We detect file creation as a signal that measurement has 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # started. In order to avoid various race conditions in tempfile creation 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # we create a temp directory and have powermetrics create it's output 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # there rather than say, creating a tempfile, deleting it and reusing its 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # name. 40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._output_directory = tempfile.mkdtemp() 41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._output_filename = os.path.join(self._output_directory, 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 'powermetrics.output') 43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) args = ['-f', 'plist', 44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) '-u', self._output_filename, 45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) '-i0', 46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) '--show-usage-summary'] 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._powermetrics_process = self._backend.LaunchApplication( 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self.binary_path, args, elevate_privilege=True) 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Block until output file is written to ensure this function call is 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # synchronous in respect to powermetrics starting. 52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def _OutputFileExists(): 53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return os.path.isfile(self._output_filename) 54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) util.WaitFor(_OutputFileExists, 1) 55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @decorators.Cache 57c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def CanMonitorPower(self): 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) mavericks_or_later = ( 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self._backend.GetOSVersionName() >= platform_backend.MAVERICKS) 60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) binary_path = self.binary_path 61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return mavericks_or_later and self._backend.CanLaunchApplication( 62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) binary_path) 63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @staticmethod 65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def _ParsePlistString(plist_string): 66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Wrapper to parse a plist from a string and catch any errors. 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Sometimes powermetrics will exit in the middle of writing it's output, 69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) empirically it seems that it always writes at least one sample in it's 70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) entirety so we can safely ignore any errors in it's output. 71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Returns: 73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Parser output on succesful parse, None on parse error. 74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try: 76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return plistlib.readPlistFromString(plist_string) 77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) except xml.parsers.expat.ExpatError: 78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return None 79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @staticmethod 81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def ParsePowerMetricsOutput(powermetrics_output): 82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Parse output of powermetrics command line utility. 83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Returns: 85c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch Dictionary in the format returned by StopMonitoringPower() or None 86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if |powermetrics_output| is empty - crbug.com/353250 . 87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if len(powermetrics_output) == 0: 89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch logging.warning("powermetrics produced zero length output") 90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return None 91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Container to collect samples for running averages. 93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # out_path - list containing the key path in the output dictionary. 94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # src_path - list containing the key path to get the data from in 95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # powermetrics' output. 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def ConstructMetric(out_path, src_path): 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) RunningAverage = collections.namedtuple('RunningAverage', [ 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 'out_path', 'src_path', 'samples']) 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return RunningAverage(out_path, src_path, []) 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # List of RunningAverage objects specifying metrics we want to aggregate. 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metrics = [ 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ConstructMetric( 104c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ['component_utilization', 'whole_package', 'average_frequency_hz'], 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['processor','freq_hz']), 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ConstructMetric( 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['component_utilization', 'whole_package', 'idle_percent'], 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['processor','packages', 0, 'c_state_ratio'])] 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def DataWithMetricKeyPath(metric, powermetrics_output): 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Retrieve the sample from powermetrics' output for a given metric. 112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Args: 114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metric: The RunningAverage object we want to collect a new sample for. 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) powermetrics_output: Dictionary containing powermetrics output. 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Returns: 118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) The sample corresponding to |metric|'s keypath.""" 119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Get actual data corresponding to key path. 120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out_data = powermetrics_output 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for k in metric.src_path: 122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out_data = out_data[k] 123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert type(out_data) in [int, float], ( 125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "Was expecting a number: %s (%s)" % (type(out_data), out_data)) 126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return float(out_data) 127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) sample_durations = [] 129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) total_energy_consumption_mwh = 0 130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # powermetrics outputs multiple plists separated by null terminators. 131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) raw_plists = powermetrics_output.split('\0') 132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) raw_plists = [x for x in raw_plists if len(x) > 0] 133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) assert(len(raw_plists) == 1) 134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # -------- Examine contents of first plist for systems specs. -------- 136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0]) 137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if not plist: 138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) logging.warning("powermetrics produced invalid output, output length: " 139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "%d" % len(powermetrics_output)) 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return {} 141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if 'GPU' in plist: 143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metrics.extend([ 144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ConstructMetric( 145c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch ['component_utilization', 'gpu', 'average_frequency_hz'], 146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['GPU', 0, 'freq_hz']), 147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ConstructMetric( 148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['component_utilization', 'gpu', 'idle_percent'], 149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ['GPU', 0, 'c_state_ratio'])]) 150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # There's no way of knowing ahead of time how many cpus and packages the 153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # current system has. Iterate over cores and cpus - construct metrics for 154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # each one. 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if 'processor' in plist: 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) core_dict = plist['processor']['packages'][0]['cores'] 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) num_cores = len(core_dict) 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cpu_num = 0 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for core_idx in xrange(num_cores): 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) num_cpus = len(core_dict[core_idx]['cpus']) 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base_src_path = ['processor', 'packages', 0, 'cores', core_idx] 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for cpu_idx in xrange(num_cpus): 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base_out_path = ['component_utilization', 'cpu%d' % cpu_num] 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # C State ratio is per-package, component CPUs of that package may 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # have different frequencies. 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metrics.append(ConstructMetric( 167c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch base_out_path + ['average_frequency_hz'], 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base_src_path + ['cpus', cpu_idx, 'freq_hz'])) 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metrics.append(ConstructMetric( 170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base_out_path + ['idle_percent'], 171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base_src_path + ['c_state_ratio'])) 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cpu_num += 1 173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # -------- Parse Data Out of Plists -------- 175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0]) 176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if not plist: 177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) logging.error("Error parsing plist.") 178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return {} 179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) # Duration of this sample. 181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) sample_duration_ms = int(plist['elapsed_ns']) / 10**6 182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) sample_durations.append(sample_duration_ms) 183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if 'processor' not in plist: 185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) logging.error("'processor' field not found in plist.") 186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return {} 187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) processor = plist['processor'] 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) total_energy_consumption_mwh = ( 190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) (float(processor.get('package_joules', 0)) / 3600.) * 10**3 ) 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) for m in metrics: 193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) m.samples.append(DataWithMetricKeyPath(m, plist)) 194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # -------- Collect and Process Data -------- 196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out_dict = {} 197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out_dict['identifier'] = 'powermetrics' 198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) out_dict['energy_consumption_mwh'] = total_energy_consumption_mwh 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def StoreMetricAverage(metric, sample_durations, out): 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Calculate average value of samples in a metric and store in output 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) path as specified by metric. 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Args: 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metric: A RunningAverage object containing samples to average. 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) sample_durations: A list which parallels the samples list containing 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) the time slice for each sample. 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out: The output dicat, average is stored in the location specified by 209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) metric.out_path. 210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if len(metric.samples) == 0: 212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return 213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert len(metric.samples) == len(sample_durations) 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) avg = 0 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for i in xrange(len(metric.samples)): 217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) avg += metric.samples[i] * sample_durations[i] 218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) avg /= sum(sample_durations) 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Store data in output, creating empty dictionaries as we go. 221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for k in metric.out_path[:-1]: 222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if not out.has_key(k): 223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out[k] = {} 224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out = out[k] 225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out[metric.out_path[-1]] = avg 226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for m in metrics: 228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) StoreMetricAverage(m, sample_durations, out_dict) 229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return out_dict 230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 231c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def StopMonitoringPower(self): 232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert self._powermetrics_process, ( 233c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch "StartMonitoringPower() not called.") 234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Tell powermetrics to take an immediate sample. 235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try: 236f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._powermetrics_process.terminate() 237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch (power_stdout, power_stderr) = self._powermetrics_process.communicate() 238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch returncode = self._powermetrics_process.returncode 239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert returncode in [0, -15], ( 240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """powermetrics error 241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return code=%d 242effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch stdout=(%s) 243effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch stderr=(%s)""" % (returncode, power_stdout, power_stderr)) 244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) with open(self._output_filename, 'rb') as output_file: 246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) powermetrics_output = output_file.read() 247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return PowerMetricsPowerMonitor.ParsePowerMetricsOutput( 248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) powermetrics_output) 249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) finally: 251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) shutil.rmtree(self._output_directory) 252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._output_directory = None 253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._output_filename = None 254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._powermetrics_process = None 255