| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 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 """Utilities for iotop/top style profiling for android.""" | |
| 6 | |
| 7 import collections | |
| 8 import json | |
| 9 import os | |
| 10 import subprocess | |
| 11 import sys | |
| 12 import urllib | |
| 13 | |
| 14 import constants | |
| 15 import io_stats_parser | |
| 16 | |
| 17 | |
| 18 class DeviceStatsMonitor(object): | |
| 19 """Class for collecting device stats such as IO/CPU usage. | |
| 20 | |
| 21 Args: | |
| 22 adb: Instance of AndroidComannds. | |
| 23 hz: Frequency at which to sample device stats. | |
| 24 """ | |
| 25 | |
| 26 DEVICE_PATH = constants.TEST_EXECUTABLE_DIR + '/device_stats_monitor' | |
| 27 PROFILE_PATH = (constants.DEVICE_PERF_OUTPUT_DIR + | |
| 28 '/device_stats_monitor.profile') | |
| 29 RESULT_VIEWER_PATH = os.path.abspath(os.path.join( | |
| 30 os.path.dirname(os.path.realpath(__file__)), 'device_stats_monitor.html')) | |
| 31 | |
| 32 def __init__(self, adb, hz): | |
| 33 self._adb = adb | |
| 34 host_path = os.path.join( | |
| 35 constants.GetOutDirectory(), 'device_stats_monitor') | |
| 36 self._adb.PushIfNeeded(host_path, DeviceStatsMonitor.DEVICE_PATH) | |
| 37 self._hz = hz | |
| 38 | |
| 39 def Start(self): | |
| 40 """Starts device stats monitor on the device.""" | |
| 41 self._adb.SetProtectedFileContents(DeviceStatsMonitor.PROFILE_PATH, '') | |
| 42 self._process = subprocess.Popen( | |
| 43 ['adb', 'shell', '%s --hz=%d %s' % ( | |
| 44 DeviceStatsMonitor.DEVICE_PATH, self._hz, | |
| 45 DeviceStatsMonitor.PROFILE_PATH)]) | |
| 46 | |
| 47 def StopAndCollect(self, output_path): | |
| 48 """Stops monitoring and saves results. | |
| 49 | |
| 50 Args: | |
| 51 output_path: Path to save results. | |
| 52 | |
| 53 Returns: | |
| 54 String of URL to load results in browser. | |
| 55 """ | |
| 56 assert self._process | |
| 57 self._adb.KillAll(DeviceStatsMonitor.DEVICE_PATH) | |
| 58 self._process.wait() | |
| 59 profile = self._adb.GetFileContents(DeviceStatsMonitor.PROFILE_PATH) | |
| 60 | |
| 61 results = collections.defaultdict(list) | |
| 62 last_io_stats = None | |
| 63 last_cpu_stats = None | |
| 64 for line in profile: | |
| 65 if ' mmcblk0 ' in line: | |
| 66 stats = io_stats_parser.ParseIoStatsLine(line) | |
| 67 if last_io_stats: | |
| 68 results['sectors_read'].append(stats.num_sectors_read - | |
| 69 last_io_stats.num_sectors_read) | |
| 70 results['sectors_written'].append(stats.num_sectors_written - | |
| 71 last_io_stats.num_sectors_written) | |
| 72 last_io_stats = stats | |
| 73 elif line.startswith('cpu '): | |
| 74 stats = self._ParseCpuStatsLine(line) | |
| 75 if last_cpu_stats: | |
| 76 results['user'].append(stats.user - last_cpu_stats.user) | |
| 77 results['nice'].append(stats.nice - last_cpu_stats.nice) | |
| 78 results['system'].append(stats.system - last_cpu_stats.system) | |
| 79 results['idle'].append(stats.idle - last_cpu_stats.idle) | |
| 80 results['iowait'].append(stats.iowait - last_cpu_stats.iowait) | |
| 81 results['irq'].append(stats.irq - last_cpu_stats.irq) | |
| 82 results['softirq'].append(stats.softirq- last_cpu_stats.softirq) | |
| 83 last_cpu_stats = stats | |
| 84 units = { | |
| 85 'sectors_read': 'sectors', | |
| 86 'sectors_written': 'sectors', | |
| 87 'user': 'jiffies', | |
| 88 'nice': 'jiffies', | |
| 89 'system': 'jiffies', | |
| 90 'idle': 'jiffies', | |
| 91 'iowait': 'jiffies', | |
| 92 'irq': 'jiffies', | |
| 93 'softirq': 'jiffies', | |
| 94 } | |
| 95 with open(output_path, 'w') as f: | |
| 96 f.write('display(%d, %s, %s);' % (self._hz, json.dumps(results), units)) | |
| 97 return 'file://%s?results=file://%s' % ( | |
| 98 DeviceStatsMonitor.RESULT_VIEWER_PATH, urllib.quote(output_path)) | |
| 99 | |
| 100 | |
| 101 @staticmethod | |
| 102 def _ParseCpuStatsLine(line): | |
| 103 """Parses a line of cpu stats into a CpuStats named tuple.""" | |
| 104 # Field definitions: http://www.linuxhowtos.org/System/procstat.htm | |
| 105 cpu_stats = collections.namedtuple('CpuStats', | |
| 106 ['device', | |
| 107 'user', | |
| 108 'nice', | |
| 109 'system', | |
| 110 'idle', | |
| 111 'iowait', | |
| 112 'irq', | |
| 113 'softirq', | |
| 114 ]) | |
| 115 fields = line.split() | |
| 116 return cpu_stats._make([fields[0]] + [int(f) for f in fields[1:8]]) | |
| OLD | NEW |