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 = '/data/local/tmp/device_stats_monitor' |
| 27 HOST_PATH = os.path.abspath(os.path.join( |
| 28 constants.CHROME_DIR, 'out', 'Release', 'device_stats_monitor')) |
| 29 PROFILE_PATH = '/sdcard/Download/device_stats_monitor.profile' |
| 30 RESULT_VIEWER_PATH = os.path.abspath(os.path.join( |
| 31 os.path.dirname(os.path.realpath(__file__)), 'device_stats_monitor.html')) |
| 32 |
| 33 def __init__(self, adb, hz): |
| 34 self._adb = adb |
| 35 self._adb.PushIfNeeded(DeviceStatsMonitor.HOST_PATH, |
| 36 DeviceStatsMonitor.DEVICE_PATH) |
| 37 self._hz = hz |
| 38 |
| 39 def Start(self): |
| 40 """Starts device stats monitor on the device.""" |
| 41 self._adb.SetFileContents(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 |