Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Unified Diff: tools/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py

Issue 1647513002: Delete tools/telemetry. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py
diff --git a/tools/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py b/tools/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py
deleted file mode 100644
index aa8687651cbc5c31e4156442f244b7c29d32ff46..0000000000000000000000000000000000000000
--- a/tools/telemetry/telemetry/internal/platform/power_monitor/powermetrics_power_monitor.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import collections
-import logging
-import os
-import plistlib
-import shutil
-import tempfile
-import xml.parsers.expat
-
-from telemetry.core import os_version
-from telemetry.core import util
-from telemetry import decorators
-from telemetry.internal.platform import power_monitor
-
-
-# TODO: rename this class (seems like this is used by mac)
-class PowerMetricsPowerMonitor(power_monitor.PowerMonitor):
-
- def __init__(self, backend):
- super(PowerMetricsPowerMonitor, self).__init__()
- self._powermetrics_process = None
- self._backend = backend
- self._output_filename = None
- self._output_directory = None
-
- @property
- def binary_path(self):
- return '/usr/bin/powermetrics'
-
- def StartMonitoringPower(self, browser):
- self._CheckStart()
- # Empirically powermetrics creates an empty output file immediately upon
- # starting. We detect file creation as a signal that measurement has
- # started. In order to avoid various race conditions in tempfile creation
- # we create a temp directory and have powermetrics create it's output
- # there rather than say, creating a tempfile, deleting it and reusing its
- # name.
- self._output_directory = tempfile.mkdtemp()
- self._output_filename = os.path.join(self._output_directory,
- 'powermetrics.output')
- args = ['-f', 'plist',
- '-u', self._output_filename,
- '-i0',
- '--show-usage-summary']
- self._powermetrics_process = self._backend.LaunchApplication(
- self.binary_path, args, elevate_privilege=True)
-
- # Block until output file is written to ensure this function call is
- # synchronous in respect to powermetrics starting.
- def _OutputFileExists():
- return os.path.isfile(self._output_filename)
- util.WaitFor(_OutputFileExists, 1)
-
- @decorators.Cache
- def CanMonitorPower(self):
- mavericks_or_later = (
- self._backend.GetOSVersionName() >= os_version.MAVERICKS)
- binary_path = self.binary_path
- return mavericks_or_later and self._backend.CanLaunchApplication(
- binary_path)
-
- @staticmethod
- def _ParsePlistString(plist_string):
- """Wrapper to parse a plist from a string and catch any errors.
-
- Sometimes powermetrics will exit in the middle of writing it's output,
- empirically it seems that it always writes at least one sample in it's
- entirety so we can safely ignore any errors in it's output.
-
- Returns:
- Parser output on successful parse, None on parse error.
- """
- try:
- return plistlib.readPlistFromString(plist_string)
- except xml.parsers.expat.ExpatError:
- return None
-
- @staticmethod
- def ParsePowerMetricsOutput(powermetrics_output):
- """Parse output of powermetrics command line utility.
-
- Returns:
- Dictionary in the format returned by StopMonitoringPower() or None
- if |powermetrics_output| is empty - crbug.com/353250 .
- """
- if len(powermetrics_output) == 0:
- logging.warning('powermetrics produced zero length output')
- return {}
-
- # Container to collect samples for running averages.
- # out_path - list containing the key path in the output dictionary.
- # src_path - list containing the key path to get the data from in
- # powermetrics' output.
- def ConstructMetric(out_path, src_path):
- RunningAverage = collections.namedtuple('RunningAverage', [
- 'out_path', 'src_path', 'samples'])
- return RunningAverage(out_path, src_path, [])
-
- # List of RunningAverage objects specifying metrics we want to aggregate.
- metrics = [
- ConstructMetric(
- ['platform_info', 'average_frequency_hz'],
- ['processor', 'freq_hz']),
- ConstructMetric(
- ['platform_info', 'idle_percent'],
- ['processor', 'packages', 0, 'c_state_ratio'])]
-
- def DataWithMetricKeyPath(metric, powermetrics_output):
- """Retrieve the sample from powermetrics' output for a given metric.
-
- Args:
- metric: The RunningAverage object we want to collect a new sample for.
- powermetrics_output: Dictionary containing powermetrics output.
-
- Returns:
- The sample corresponding to |metric|'s keypath."""
- # Get actual data corresponding to key path.
- out_data = powermetrics_output
- for k in metric.src_path:
- out_data = out_data[k]
-
- assert type(out_data) in [int, float], (
- 'Was expecting a number: %s (%s)' % (type(out_data), out_data))
- return float(out_data)
-
- sample_durations = []
- total_energy_consumption_mwh = 0
- # powermetrics outputs multiple plists separated by null terminators.
- raw_plists = powermetrics_output.split('\0')
- raw_plists = [x for x in raw_plists if len(x) > 0]
- assert len(raw_plists) == 1
-
- # -------- Examine contents of first plist for systems specs. --------
- plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0])
- if not plist:
- logging.warning('powermetrics produced invalid output, output length: '
- '%d', len(powermetrics_output))
- return {}
-
- # Powermetrics doesn't record power usage when running on a VM.
- hw_model = plist.get('hw_model')
- if hw_model and hw_model.startswith('VMware'):
- return {}
-
- if 'GPU' in plist:
- metrics.extend([
- ConstructMetric(
- ['component_utilization', 'gpu', 'average_frequency_hz'],
- ['GPU', 0, 'freq_hz']),
- ConstructMetric(
- ['component_utilization', 'gpu', 'idle_percent'],
- ['GPU', 0, 'c_state_ratio'])])
-
- # There's no way of knowing ahead of time how many cpus and packages the
- # current system has. Iterate over cores and cpus - construct metrics for
- # each one.
- if 'processor' in plist:
- core_dict = plist['processor']['packages'][0]['cores']
- num_cores = len(core_dict)
- cpu_num = 0
- for core_idx in xrange(num_cores):
- num_cpus = len(core_dict[core_idx]['cpus'])
- base_src_path = ['processor', 'packages', 0, 'cores', core_idx]
- for cpu_idx in xrange(num_cpus):
- base_out_path = ['component_utilization', 'cpu%d' % cpu_num]
- # C State ratio is per-package, component CPUs of that package may
- # have different frequencies.
- metrics.append(ConstructMetric(
- base_out_path + ['average_frequency_hz'],
- base_src_path + ['cpus', cpu_idx, 'freq_hz']))
- metrics.append(ConstructMetric(
- base_out_path + ['idle_percent'],
- base_src_path + ['c_state_ratio']))
- cpu_num += 1
-
- # -------- Parse Data Out of Plists --------
- plist = PowerMetricsPowerMonitor._ParsePlistString(raw_plists[0])
- if not plist:
- logging.error('Error parsing plist.')
- return {}
-
- # Duration of this sample.
- sample_duration_ms = int(plist['elapsed_ns']) / 10 ** 6
- sample_durations.append(sample_duration_ms)
-
- if 'processor' not in plist:
- logging.error("'processor' field not found in plist.")
- return {}
- processor = plist['processor']
-
- total_energy_consumption_mwh = (
- (float(processor.get('package_joules', 0)) / 3600.) * 10 ** 3)
-
- for m in metrics:
- try:
- m.samples.append(DataWithMetricKeyPath(m, plist))
- except KeyError:
- # Old CPUs don't have c-states, so if data is missing, just ignore it.
- logging.info('Field missing from powermetrics output: %s', m.src_path)
- continue
-
- # -------- Collect and Process Data --------
- out_dict = {}
- out_dict['identifier'] = 'powermetrics'
- out_dict['energy_consumption_mwh'] = total_energy_consumption_mwh
-
- def StoreMetricAverage(metric, sample_durations, out):
- """Calculate average value of samples in a metric and store in output
- path as specified by metric.
-
- Args:
- metric: A RunningAverage object containing samples to average.
- sample_durations: A list which parallels the samples list containing
- the time slice for each sample.
- out: The output dicat, average is stored in the location specified by
- metric.out_path.
- """
- if len(metric.samples) == 0:
- return
-
- assert len(metric.samples) == len(sample_durations)
- avg = 0
- for i in xrange(len(metric.samples)):
- avg += metric.samples[i] * sample_durations[i]
- avg /= sum(sample_durations)
-
- # Store data in output, creating empty dictionaries as we go.
- for k in metric.out_path[:-1]:
- if not out.has_key(k):
- out[k] = {}
- out = out[k]
- out[metric.out_path[-1]] = avg
-
- for m in metrics:
- StoreMetricAverage(m, sample_durations, out_dict)
- return out_dict
-
- def _KillPowerMetricsProcess(self):
- """Kill a running powermetrics process."""
- try:
- if self._powermetrics_process.poll() is None:
- self._powermetrics_process.terminate()
- except OSError as e:
- logging.warning(
- 'Error when trying to terminate powermetric process: %s', repr(e))
- if self._powermetrics_process.poll() is None:
- # terminate() can fail when Powermetrics does not have the SetUID set.
- self._backend.LaunchApplication(
- '/usr/bin/pkill',
- ['-SIGTERM', os.path.basename(self.binary_path)],
- elevate_privilege=True)
-
- def StopMonitoringPower(self):
- self._CheckStop()
- # Tell powermetrics to take an immediate sample.
- try:
- self._KillPowerMetricsProcess()
- (power_stdout, power_stderr) = self._powermetrics_process.communicate()
- returncode = self._powermetrics_process.returncode
- assert returncode in [0, -15], (
- """powermetrics error
- return code=%d
- stdout=(%s)
- stderr=(%s)""" % (returncode, power_stdout, power_stderr))
-
- with open(self._output_filename, 'rb') as output_file:
- powermetrics_output = output_file.read()
- return PowerMetricsPowerMonitor.ParsePowerMetricsOutput(
- powermetrics_output)
- except Exception as e:
- logging.warning(
- 'Error when trying to collect power monitoring data: %s', repr(e))
- return PowerMetricsPowerMonitor.ParsePowerMetricsOutput('')
- finally:
- shutil.rmtree(self._output_directory)
- self._output_directory = None
- self._output_filename = None
- self._powermetrics_process = None

Powered by Google App Engine
This is Rietveld 408576698