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

Unified Diff: tools/android/activity_monitor/activity_monitor.py

Issue 10783020: Add an activity monitor which profiles IO and CPU utilization. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 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/android/activity_monitor/activity_monitor.py
diff --git a/tools/android/activity_monitor/activity_monitor.py b/tools/android/activity_monitor/activity_monitor.py
new file mode 100755
index 0000000000000000000000000000000000000000..2b45c151a4817116234b77b3813bab676511f68b
--- /dev/null
+++ b/tools/android/activity_monitor/activity_monitor.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Provides iotop/top style profiling for android.
+
+Usage:
+ ./activity_monitor.py --hz=20 --duration=5 --outfile=/tmp/foo
+"""
+
+import collections
+import json
+import optparse
+import os
+import subprocess
+import sys
+import time
+import urllib
+
+sys.path.append(os.path.join(sys.path[0], '..', '..', '..', 'build','android'))
+from pylib import android_commands
bulach 2012/07/16 17:52:26 nit: while at it, from pylib import constants
+from pylib import io_stats_parser
+
+
+ACTIVITY_MONITOR_DEVICE_PATH = '/data/local/tmp/activity_monitor'
+ACTIVITY_MONITOR_HOST_PATH = os.path.abspath(os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), '..', '..', '..',
bulach 2012/07/16 17:52:26 nit: pylib.constants.CHROME_DIR
+ 'out', 'Release', 'activity_monitor'))
+PROFILE_PATH = '/sdcard/Download/activity_monitor.profile'
+RESULT_VIEWER_PATH = os.path.abspath(os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), 'activity_monitor.html'))
+
+
+class ActivityMonitor(object):
+
+ def __init__(self, adb=None, hz=20):
bulach 2012/07/16 17:52:26 I just noticed that this is used by build/android/
+ """Create an ActivityMonitor
+
+ Args
+ adb: Instance of AndroidComannds.
+ hz: Frequency at which to sample activity.
+ """
+ self._adb = adb or android_commands.AndroidCommands()
+ self._adb.PushIfNeeded(ACTIVITY_MONITOR_HOST_PATH,
+ ACTIVITY_MONITOR_DEVICE_PATH)
+ self._hz = hz
+
+ def Start(self):
+ """Starts ActivityMonitor on the device."""
+ self._adb.SetFileContents(PROFILE_PATH, '')
+ self._process = subprocess.Popen(
+ ['adb', 'shell', '%s --hz=%d %s' % (
+ ACTIVITY_MONITOR_DEVICE_PATH, self._hz, 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(ACTIVITY_MONITOR_DEVICE_PATH)
+ self._process.wait()
+ profile = self._adb.GetFileContents(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' % (RESULT_VIEWER_PATH,
+ urllib.quote(output_path))
+
+
+ @staticmethod
+ def ParseCpuStatsLine(line):
bulach 2012/07/16 17:52:26 nit: add a _ prefix
+ """Parses a line of cpu stats into a CpuStats named tuple."""
+ # Field definitions: http://www.linuxhowtos.org/System/procstat.htm
+ CpuStats = collections.namedtuple('CpuStats',
bulach 2012/07/16 17:52:26 nit: cpu_stats
+ ['device',
+ 'user',
+ 'nice',
+ 'system',
+ 'idle',
+ 'iowait',
+ 'irq',
+ 'softirq',
+ ])
+ fields = line.split()
+ return CpuStats._make([fields[0]] + [int(f) for f in fields[1:8]])
+
+
+def main(argv):
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('--hz', type='int', default=20,
+ help='Number of samples/sec.')
+ option_parser.add_option('--duration', type='int', default=5,
+ help='Seconds to monitor.')
+ option_parser.add_option('--outfile', default='/tmp/activitymonitor',
+ help='Location to start output file.')
+ options, args = option_parser.parse_args(argv)
+
+ activity_monitor = ActivityMonitor(hz=options.hz)
+ activity_monitor.Start()
+ print 'Waiting for %d seconds while profiling.' % options.duration
+ time.sleep(options.duration)
+ url = activity_monitor.StopAndCollect(options.outfile)
+ print 'View results in browser at %s' % url
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))

Powered by Google App Engine
This is Rietveld 408576698