Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / platform / power_monitor / android_ds2784_power_monitor.py
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import logging
6 import os
7
8 from telemetry import decorators
9 from telemetry.core.platform.power_monitor import sysfs_power_monitor
10 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
11
12
13 SAMPLE_RATE_HZ = 2 # The data is collected from the ds2784 fuel gauge chip
14                    # that only updates its data every 3.5s.
15 FUEL_GAUGE_PATH = '/sys/class/power_supply/ds2784-fuelgauge'
16 CHARGE_COUNTER = os.path.join(FUEL_GAUGE_PATH, 'charge_counter_ext')
17 CURRENT = os.path.join(FUEL_GAUGE_PATH, 'current_now')
18 VOLTAGE = os.path.join(FUEL_GAUGE_PATH, 'voltage_now')
19
20
21 class DS2784PowerMonitor(sysfs_power_monitor.SysfsPowerMonitor):
22   def __init__(self, device, platform_backend):
23     super(DS2784PowerMonitor, self).__init__(platform_backend)
24     self._device = device
25     self._powermonitor_process_port = None
26     self._file_poller_binary = android_prebuilt_profiler_helper.GetDevicePath(
27         'file_poller')
28
29   @decorators.Cache
30   def _HasFuelGauge(self):
31     return self._device.FileExists(CHARGE_COUNTER)
32
33   def CanMonitorPower(self):
34     if not self._HasFuelGauge():
35       return False
36     if self._device.old_interface.IsDeviceCharging():
37       logging.warning('Can\'t monitor power usage since device is charging.')
38       return False
39     return True
40
41   def StartMonitoringPower(self, browser):
42     assert not self._powermonitor_process_port, (
43         'Must call StopMonitoringPower().')
44     super(DS2784PowerMonitor, self).StartMonitoringPower(browser)
45     android_prebuilt_profiler_helper.InstallOnDevice(
46         self._device, 'file_poller')
47     self._powermonitor_process_port = int(
48         self._device.RunShellCommand(
49             '%s %d %s %s %s' % (self._file_poller_binary, SAMPLE_RATE_HZ,
50                                 CHARGE_COUNTER, CURRENT, VOLTAGE))[0])
51
52   def StopMonitoringPower(self):
53     assert self._powermonitor_process_port, (
54         'StartMonitoringPower() not called.')
55     try:
56       cpu_stats = super(DS2784PowerMonitor, self).StopMonitoringPower()
57       result = '\n'.join(self._device.RunShellCommand(
58           '%s %d' % (self._file_poller_binary,
59                      self._powermonitor_process_port)))
60       assert result, 'PowerMonitor produced no output'
61       return super(DS2784PowerMonitor, self).CombineResults(
62           cpu_stats, DS2784PowerMonitor.ParseSamplingOutput(result))
63     finally:
64       self._powermonitor_process_port = None
65
66   @staticmethod
67   def ParseSamplingOutput(powermonitor_output):
68     """Parse output of powermonitor command line utility.
69
70     Returns:
71         Dictionary in the format returned by StopMonitoringPower().
72     """
73     power_samples = []
74     total_energy_consumption_mwh = 0
75     def ParseSample(sample):
76       values = [float(x) for x in sample.split(' ')]
77       res = {}
78       (res['timestamp_s'],
79        res['charge_nah'],
80        res['current_ua'],
81        res['voltage_uv']) = values
82       return res
83     # The output contains a sample per line.
84     samples = map(ParseSample, powermonitor_output.split('\n')[:-1])
85     # Keep track of the last sample that found an updated reading.
86     last_updated_sample = samples[0]
87     # Compute average voltage.
88     voltage_sum_uv = 0
89     voltage_count = 0
90     for sample in samples:
91       if sample['charge_nah'] != last_updated_sample['charge_nah']:
92         charge_difference_nah = (sample['charge_nah'] -
93                                  last_updated_sample['charge_nah'])
94         # Use average voltage for the energy consumption.
95         voltage_sum_uv += sample['voltage_uv']
96         voltage_count += 1
97         average_voltage_uv = voltage_sum_uv / voltage_count
98         total_energy_consumption_mwh += (-charge_difference_nah *
99                                          average_voltage_uv / 10 ** 12)
100         last_updated_sample = sample
101         voltage_sum_uv = 0
102         voltage_count = 0
103       # Update average voltage.
104       voltage_sum_uv += sample['voltage_uv']
105       voltage_count += 1
106       # Compute energy of the sample.
107       energy_consumption_mw = (-sample['current_ua'] * sample['voltage_uv'] /
108                                10 ** 9)
109
110       power_samples.append(energy_consumption_mw)
111     # Because the data is stalled for a few seconds, compute the remaining
112     # energy consumption using the last available current reading.
113     last_sample = samples[-1]
114     remaining_time_h = (
115         last_sample['timestamp_s'] - last_updated_sample['timestamp_s']) / 3600
116     average_voltage_uv = voltage_sum_uv / voltage_count
117
118     remaining_energy_consumption_mwh = (-last_updated_sample['current_ua'] *
119                                         average_voltage_uv *
120                                         remaining_time_h  / 10 ** 9)
121     total_energy_consumption_mwh += remaining_energy_consumption_mwh
122
123     # -------- Collect and Process Data -------------
124     out_dict = {}
125     # Raw power usage samples.
126     out_dict['identifier'] = 'ds2784'
127     out_dict['power_samples_mw'] = power_samples
128     out_dict['energy_consumption_mwh'] = total_energy_consumption_mwh
129
130     return out_dict