Index: build/android/pylib/device_stats_monitor.py |
diff --git a/build/android/pylib/device_stats_monitor.py b/build/android/pylib/device_stats_monitor.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..79225028baf7c19ac51276b956181e1ab8eb33da |
--- /dev/null |
+++ b/build/android/pylib/device_stats_monitor.py |
@@ -0,0 +1,116 @@ |
+# Copyright (c) 2012 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. |
+ |
+"""Utilities for iotop/top style profiling for android.""" |
+ |
+import collections |
+import json |
+import os |
+import subprocess |
+import sys |
+import urllib |
+ |
+import constants |
+import io_stats_parser |
+ |
+ |
+class DeviceStatsMonitor(object): |
+ """Class for collecting device stats such as IO/CPU usage. |
+ |
+ Args: |
+ adb: Instance of AndroidComannds. |
+ hz: Frequency at which to sample device stats. |
+ """ |
+ |
+ DEVICE_PATH = '/data/local/tmp/device_stats_monitor' |
+ HOST_PATH = os.path.abspath(os.path.join( |
+ constants.CHROME_DIR, 'out', 'Release', 'device_stats_monitor')) |
+ PROFILE_PATH = '/sdcard/Download/device_stats_monitor.profile' |
+ RESULT_VIEWER_PATH = os.path.abspath(os.path.join( |
+ os.path.dirname(os.path.realpath(__file__)), 'device_stats_monitor.html')) |
+ |
+ def __init__(self, adb, hz): |
+ self._adb = adb |
+ self._adb.PushIfNeeded(DeviceStatsMonitor.HOST_PATH, |
+ DeviceStatsMonitor.DEVICE_PATH) |
+ self._hz = hz |
+ |
+ def Start(self): |
+ """Starts device stats monitor on the device.""" |
+ self._adb.SetFileContents(DeviceStatsMonitor.PROFILE_PATH, '') |
+ self._process = subprocess.Popen( |
+ ['adb', 'shell', '%s --hz=%d %s' % ( |
+ DeviceStatsMonitor.DEVICE_PATH, self._hz, |
+ DeviceStatsMonitor.PROFILE_PATH)]) |
+ |
+ def StopAndCollect(self, output_path): |
+ """Stops monitoring and saves results. |
+ |
+ Args: |
+ output_path: Path to save results. |
+ |
+ Returns: |
+ String of URL to load results in browser. |
+ """ |
+ assert self._process |
+ self._adb.KillAll(DeviceStatsMonitor.DEVICE_PATH) |
+ self._process.wait() |
+ profile = self._adb.GetFileContents(DeviceStatsMonitor.PROFILE_PATH) |
+ |
+ results = collections.defaultdict(list) |
+ last_io_stats = None |
+ last_cpu_stats = None |
+ for line in profile: |
+ if ' mmcblk0 ' in line: |
+ stats = io_stats_parser.ParseIoStatsLine(line) |
+ if last_io_stats: |
+ results['sectors_read'].append(stats.num_sectors_read - |
+ last_io_stats.num_sectors_read) |
+ results['sectors_written'].append(stats.num_sectors_written - |
+ last_io_stats.num_sectors_written) |
+ last_io_stats = stats |
+ elif line.startswith('cpu '): |
+ stats = self._ParseCpuStatsLine(line) |
+ if last_cpu_stats: |
+ results['user'].append(stats.user - last_cpu_stats.user) |
+ results['nice'].append(stats.nice - last_cpu_stats.nice) |
+ results['system'].append(stats.system - last_cpu_stats.system) |
+ results['idle'].append(stats.idle - last_cpu_stats.idle) |
+ results['iowait'].append(stats.iowait - last_cpu_stats.iowait) |
+ results['irq'].append(stats.irq - last_cpu_stats.irq) |
+ results['softirq'].append(stats.softirq- last_cpu_stats.softirq) |
+ last_cpu_stats = stats |
+ units = { |
+ 'sectors_read': 'sectors', |
+ 'sectors_written': 'sectors', |
+ 'user': 'jiffies', |
+ 'nice': 'jiffies', |
+ 'system': 'jiffies', |
+ 'idle': 'jiffies', |
+ 'iowait': 'jiffies', |
+ 'irq': 'jiffies', |
+ 'softirq': 'jiffies', |
+ } |
+ with open(output_path, 'w') as f: |
+ f.write('display(%d, %s, %s);' % (self._hz, json.dumps(results), units)) |
+ return 'file://%s?results=file://%s' % ( |
+ DeviceStatsMonitor.RESULT_VIEWER_PATH, urllib.quote(output_path)) |
+ |
+ |
+ @staticmethod |
+ def _ParseCpuStatsLine(line): |
+ """Parses a line of cpu stats into a CpuStats named tuple.""" |
+ # Field definitions: http://www.linuxhowtos.org/System/procstat.htm |
+ cpu_stats = collections.namedtuple('CpuStats', |
+ ['device', |
+ 'user', |
+ 'nice', |
+ 'system', |
+ 'idle', |
+ 'iowait', |
+ 'irq', |
+ 'softirq', |
+ ]) |
+ fields = line.split() |
+ return cpu_stats._make([fields[0]] + [int(f) for f in fields[1:8]]) |