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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 """Provides iotop/top style profiling for android.
8
9 Usage:
10 ./activity_monitor.py --hz=20 --duration=5 --outfile=/tmp/foo
11 """
12
13 import collections
14 import json
15 import optparse
16 import os
17 import subprocess
18 import sys
19 import time
20 import urllib
21
22 sys.path.append(os.path.join(sys.path[0], '..', '..', '..', 'build','android'))
23 from pylib import android_commands
bulach 2012/07/16 17:52:26 nit: while at it, from pylib import constants
24 from pylib import io_stats_parser
25
26
27 ACTIVITY_MONITOR_DEVICE_PATH = '/data/local/tmp/activity_monitor'
28 ACTIVITY_MONITOR_HOST_PATH = os.path.abspath(os.path.join(
29 os.path.dirname(os.path.realpath(__file__)), '..', '..', '..',
bulach 2012/07/16 17:52:26 nit: pylib.constants.CHROME_DIR
30 'out', 'Release', 'activity_monitor'))
31 PROFILE_PATH = '/sdcard/Download/activity_monitor.profile'
32 RESULT_VIEWER_PATH = os.path.abspath(os.path.join(
33 os.path.dirname(os.path.realpath(__file__)), 'activity_monitor.html'))
34
35
36 class ActivityMonitor(object):
37
38 def __init__(self, adb=None, hz=20):
bulach 2012/07/16 17:52:26 I just noticed that this is used by build/android/
39 """Create an ActivityMonitor
40
41 Args
42 adb: Instance of AndroidComannds.
43 hz: Frequency at which to sample activity.
44 """
45 self._adb = adb or android_commands.AndroidCommands()
46 self._adb.PushIfNeeded(ACTIVITY_MONITOR_HOST_PATH,
47 ACTIVITY_MONITOR_DEVICE_PATH)
48 self._hz = hz
49
50 def Start(self):
51 """Starts ActivityMonitor on the device."""
52 self._adb.SetFileContents(PROFILE_PATH, '')
53 self._process = subprocess.Popen(
54 ['adb', 'shell', '%s --hz=%d %s' % (
55 ACTIVITY_MONITOR_DEVICE_PATH, self._hz, PROFILE_PATH)])
56
57 def StopAndCollect(self, output_path):
58 """Stops monitoring and saves results.
59
60 Args:
61 output_path: Path to save results.
62
63 Returns:
64 String of URL to load results in browser.
65 """
66 assert self._process
67 self._adb.KillAll(ACTIVITY_MONITOR_DEVICE_PATH)
68 self._process.wait()
69 profile = self._adb.GetFileContents(PROFILE_PATH)
70
71 results = collections.defaultdict(list)
72 last_io_stats = None
73 last_cpu_stats = None
74 for line in profile:
75 if ' mmcblk0 ' in line:
76 stats = io_stats_parser.ParseIoStatsLine(line)
77 if last_io_stats:
78 results['sectors_read'].append(stats.num_sectors_read -
79 last_io_stats.num_sectors_read)
80 results['sectors_written'].append(stats.num_sectors_written -
81 last_io_stats.num_sectors_written)
82 last_io_stats = stats
83 elif line.startswith('cpu '):
84 stats = self.ParseCpuStatsLine(line)
85 if last_cpu_stats:
86 results['user'].append(stats.user - last_cpu_stats.user)
87 results['nice'].append(stats.nice - last_cpu_stats.nice)
88 results['system'].append(stats.system - last_cpu_stats.system)
89 results['idle'].append(stats.idle - last_cpu_stats.idle)
90 results['iowait'].append(stats.iowait - last_cpu_stats.iowait)
91 results['irq'].append(stats.irq - last_cpu_stats.irq)
92 results['softirq'].append(stats.softirq- last_cpu_stats.softirq)
93 last_cpu_stats = stats
94 units = {
95 'sectors_read': 'sectors',
96 'sectors_written': 'sectors',
97 'user': 'jiffies',
98 'nice': 'jiffies',
99 'system': 'jiffies',
100 'idle': 'jiffies',
101 'iowait': 'jiffies',
102 'irq': 'jiffies',
103 'softirq': 'jiffies',
104 }
105 with open(output_path, 'w') as f:
106 f.write('display(%d, %s, %s);' % (self._hz, json.dumps(results), units))
107 return 'file://%s?results=file://%s' % (RESULT_VIEWER_PATH,
108 urllib.quote(output_path))
109
110
111 @staticmethod
112 def ParseCpuStatsLine(line):
bulach 2012/07/16 17:52:26 nit: add a _ prefix
113 """Parses a line of cpu stats into a CpuStats named tuple."""
114 # Field definitions: http://www.linuxhowtos.org/System/procstat.htm
115 CpuStats = collections.namedtuple('CpuStats',
bulach 2012/07/16 17:52:26 nit: cpu_stats
116 ['device',
117 'user',
118 'nice',
119 'system',
120 'idle',
121 'iowait',
122 'irq',
123 'softirq',
124 ])
125 fields = line.split()
126 return CpuStats._make([fields[0]] + [int(f) for f in fields[1:8]])
127
128
129 def main(argv):
130 option_parser = optparse.OptionParser()
131 option_parser.add_option('--hz', type='int', default=20,
132 help='Number of samples/sec.')
133 option_parser.add_option('--duration', type='int', default=5,
134 help='Seconds to monitor.')
135 option_parser.add_option('--outfile', default='/tmp/activitymonitor',
136 help='Location to start output file.')
137 options, args = option_parser.parse_args(argv)
138
139 activity_monitor = ActivityMonitor(hz=options.hz)
140 activity_monitor.Start()
141 print 'Waiting for %d seconds while profiling.' % options.duration
142 time.sleep(options.duration)
143 url = activity_monitor.StopAndCollect(options.outfile)
144 print 'View results in browser at %s' % url
145
146 if __name__ == '__main__':
147 sys.exit(main(sys.argv))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698