Index: tools/telemetry/telemetry/internal/platform/android_platform_backend.py |
diff --git a/tools/telemetry/telemetry/internal/platform/android_platform_backend.py b/tools/telemetry/telemetry/internal/platform/android_platform_backend.py |
index dc93b3062b0beea845ab095754244023b6c78d81..eaeb52e6969d55b66e41255ee010d7c1e3447021 100644 |
--- a/tools/telemetry/telemetry/internal/platform/android_platform_backend.py |
+++ b/tools/telemetry/telemetry/internal/platform/android_platform_backend.py |
@@ -27,6 +27,7 @@ from telemetry.internal.platform.power_monitor import power_monitor_controller |
from telemetry.internal.platform.profiler import android_prebuilt_profiler_helper |
from telemetry.internal.util import exception_formatter |
from telemetry.internal.util import external_modules |
+from telemetry.util import process_statistic_timeline_data |
psutil = external_modules.ImportOptionalModule('psutil') |
util.AddDirToPythonPath(util.GetChromiumSrcDir(), |
@@ -264,7 +265,11 @@ class AndroidPlatformBackend( |
if not self._can_access_protected_file_contents: |
logging.warning('CPU stats cannot be retrieved on non-rooted device.') |
return {} |
- return super(AndroidPlatformBackend, self).GetCpuStats(pid) |
+ results = super(AndroidPlatformBackend, self).GetCpuStats(pid) |
+ idle_wakeup_count = self._GetIdleWakeupCount(pid) |
+ if idle_wakeup_count: |
+ results.update({'IdleWakeupCount': idle_wakeup_count}) |
+ return results |
def GetCpuTimestamp(self): |
if not self._can_access_protected_file_contents: |
@@ -432,6 +437,9 @@ class AndroidPlatformBackend( |
def CanMonitorPower(self): |
return self._power_monitor.CanMonitorPower() |
+ def CanMeasureIdleWakeUps(self): |
+ return True |
+ |
def StartMonitoringPower(self, browser): |
self._power_monitor.StartMonitoringPower(browser) |
@@ -454,9 +462,14 @@ class AndroidPlatformBackend( |
return self._device.ReadFile(fname, as_root=True) |
def GetPsOutput(self, columns, pid=None): |
+ return self._GetPsOutput(columns=columns, pid=pid) |
+ |
+ def _GetPsOutput(self, columns, list_threads=False, pid=None): |
assert columns == ['pid', 'name'] or columns == ['pid'], \ |
'Only know how to return pid and name. Requested: ' + columns |
command = 'ps' |
+ if list_threads: |
+ command += ' -t' |
if pid: |
command += ' -p %d' % pid |
ps = self._device.RunShellCommand(command, large_output=True)[1:] |
@@ -699,6 +712,42 @@ class AndroidPlatformBackend( |
""" |
return '/data/data/%s/' % package |
+ |
+ def _GetIdleWakeupCount(self, pid): |
+ """Find the given pid in the timer_stats and sum up the wake-up count. |
+ |
+ Each row is in the form of: wake_up_count, pid process_name reason |
+ Due to possibly multiple reasons, a given pid can appear in multiple rows. |
+ """ |
+ contents = self.GetFileContents('/proc/timer_stats') |
+ if not contents: |
+ return None |
+ count = 0 |
+ split_lines = contents.splitlines() |
+ matching_pids = [] |
+ # The pid listed by timer_stats is the *kernel* pid (potentially different |
+ # for each thread), which may differ from the provided process pid. Use ps |
+ # to map each thread pid back to the proper process pid (tgid). |
+ for matching_pid in self._GetPsOutput(['pid'], list_threads=True, |
+ pid=int(pid)): |
+ matching_pids.append(matching_pid[0]) |
+ for line in split_lines[:-1]: |
+ line_split = line.split(',', 1) |
+ if len(line_split) < 2: |
+ continue |
+ count_str, rest_str = line_split |
+ current_pid = rest_str.strip().split(' ')[0] |
+ if current_pid == pid or current_pid in matching_pids: |
+ # The "D" suffix on the count indicates a "deferrable" timer. |
+ count += int(count_str.strip().rstrip('D')) |
+ return process_statistic_timeline_data.IdleWakeupTimelineData(pid, count) |
+ |
+ def _EnableTimerStatsCollection(self, enable): |
+ """Writes to /proc/timer_stats to start/stop measuring process wakeups.""" |
+ parameter = 1 if enable else 0 |
+ cmd = 'echo %d > /proc/timer_stats' % parameter |
+ self._device.RunShellCommand(cmd, as_root=True) |
+ |
def SetDebugApp(self, package): |
"""Set application to debugging. |