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

Side by Side Diff: build/android/pylib/android_commands.py

Issue 10689132: [android] Upstream / sync most of build/android and build/android/pylib. (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
« no previous file with comments | « build/android/lighttpd_server.py ('k') | build/android/pylib/base_test_runner.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Provides an interface to communicate with the device via the adb command. 5 """Provides an interface to communicate with the device via the adb command.
6 6
7 Assumes adb binary is currently on system path. 7 Assumes adb binary is currently on system path.
8
9 Usage:
10 python android_commands.py wait-for-pm
11 """ 8 """
12 9
13 import collections 10 import collections
14 import datetime 11 import datetime
12 import io_stats_parser
15 import logging 13 import logging
16 import optparse 14 import optparse
17 import os 15 import os
18 import pexpect 16 import pexpect
19 import re 17 import re
18 import shlex
20 import subprocess 19 import subprocess
21 import sys 20 import sys
22 import tempfile 21 import tempfile
23 import time 22 import time
24 23
24
25 # adb_interface.py is under ../../../third_party/android_testrunner/ 25 # adb_interface.py is under ../../../third_party/android_testrunner/
26 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 26 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..',
27 '..', '..', 'third_party', 'android_testrunner')) 27 '..', '..', 'third_party', 'android_testrunner'))
28 import adb_interface 28 import adb_interface
29 import cmd_helper 29 import cmd_helper
30 import errors # is under ../../third_party/android_testrunner/errors.py 30 import errors # is under ../../../third_party/android_testrunner/errors.py
31 from run_tests_helper import IsRunningAsBuildbot
32 31
33 32
34 # Pattern to search for the next whole line of pexpect output and capture it 33 # Pattern to search for the next whole line of pexpect output and capture it
35 # into a match group. We can't use ^ and $ for line start end with pexpect, 34 # into a match group. We can't use ^ and $ for line start end with pexpect,
36 # see http://www.noah.org/python/pexpect/#doc for explanation why. 35 # see http://www.noah.org/python/pexpect/#doc for explanation why.
37 PEXPECT_LINE_RE = re.compile('\n([^\r]*)\r') 36 PEXPECT_LINE_RE = re.compile('\n([^\r]*)\r')
38 37
39 # Set the adb shell prompt to be a unique marker that will [hopefully] not 38 # Set the adb shell prompt to be a unique marker that will [hopefully] not
40 # appear at the start of any line of a command's output. 39 # appear at the start of any line of a command's output.
41 SHELL_PROMPT = '~+~PQ\x17RS~+~' 40 SHELL_PROMPT = '~+~PQ\x17RS~+~'
42 41
43 # This only works for single core devices. 42 # This only works for single core devices.
44 SCALING_GOVERNOR = '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' 43 SCALING_GOVERNOR = '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'
45 DROP_CACHES = '/proc/sys/vm/drop_caches' 44 DROP_CACHES = '/proc/sys/vm/drop_caches'
46 45
47 # Java properties file 46 # Java properties file
48 LOCAL_PROPERTIES_PATH = '/data/local.prop' 47 LOCAL_PROPERTIES_PATH = '/data/local.prop'
49 48
50 # Property in /data/local.prop that controls Java assertions. 49 # Property in /data/local.prop that controls Java assertions.
51 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' 50 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
52 51
53 BOOT_COMPLETE_RE = re.compile( 52 BOOT_COMPLETE_RE = re.compile(
54 re.escape('android.intent.action.MEDIA_MOUNTED path: /mnt/sdcard') 53 'android.intent.action.MEDIA_MOUNTED path: /\w+/sdcard\d?'
55 + '|' + re.escape('PowerManagerService: bootCompleted')) 54 + '|' + 'PowerManagerService(\(\s+\d+\))?: bootCompleted')
55
56 MEMORY_INFO_RE = re.compile('^(?P<key>\w+):\s+(?P<usage_kb>\d+) kB$')
57 NVIDIA_MEMORY_INFO_RE = re.compile('^\s*(?P<user>\S+)\s*(?P<name>\S+)\s*'
58 '(?P<pid>\d+)\s*(?P<usage_bytes>\d+)$')
56 59
57 # Keycode "enum" suitable for passing to AndroidCommands.SendKey(). 60 # Keycode "enum" suitable for passing to AndroidCommands.SendKey().
61 KEYCODE_HOME = 3
62 KEYCODE_BACK = 4
63 KEYCODE_DPAD_UP = 19
64 KEYCODE_DPAD_DOWN = 20
58 KEYCODE_DPAD_RIGHT = 22 65 KEYCODE_DPAD_RIGHT = 22
59 KEYCODE_ENTER = 66 66 KEYCODE_ENTER = 66
60 KEYCODE_MENU = 82 67 KEYCODE_MENU = 82
61 KEYCODE_BACK = 4
62 68
63 69
64 def GetEmulators(): 70 def GetEmulators():
65 """Returns a list of emulators. Does not filter by status (e.g. offline). 71 """Returns a list of emulators. Does not filter by status (e.g. offline).
66 72
67 Both devices starting with 'emulator' will be returned in below output: 73 Both devices starting with 'emulator' will be returned in below output:
68 74
69 * daemon not running. starting it now on port 5037 * 75 * daemon not running. starting it now on port 5037 *
70 * daemon started successfully * 76 * daemon started successfully *
71 List of devices attached 77 List of devices attached
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 if isinstance(utc_offset, str) and len(utc_offset) == 5: 188 if isinstance(utc_offset, str) and len(utc_offset) == 5:
183 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]), 189 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]),
184 minutes=int(utc_offset[3:5])) 190 minutes=int(utc_offset[3:5]))
185 if utc_offset[0:1] == '-': 191 if utc_offset[0:1] == '-':
186 utc_delta = -utc_delta; 192 utc_delta = -utc_delta;
187 lastmod -= utc_delta 193 lastmod -= utc_delta
188 files[filename] = (int(file_match.group('size')), lastmod) 194 files[filename] = (int(file_match.group('size')), lastmod)
189 return files 195 return files
190 196
191 197
192 def GetLogTimestamp(log_line): 198 def GetLogTimestamp(log_line, year):
193 """Returns the timestamp of the given |log_line|.""" 199 """Returns the timestamp of the given |log_line| in the given year."""
194 try: 200 try:
195 return datetime.datetime.strptime(log_line[:18], '%m-%d %H:%M:%S.%f') 201 return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]),
202 '%Y-%m-%d %H:%M:%S.%f')
196 except (ValueError, IndexError): 203 except (ValueError, IndexError):
197 logging.critical('Error reading timestamp from ' + log_line) 204 logging.critical('Error reading timestamp from ' + log_line)
198 return None 205 return None
199 206
200 207
201 class AndroidCommands(object): 208 class AndroidCommands(object):
202 """Helper class for communicating with Android device via adb. 209 """Helper class for communicating with Android device via adb.
203 210
204 Args: 211 Args:
205 device: If given, adb commands are only send to the device of this ID. 212 device: If given, adb commands are only send to the device of this ID.
206 Otherwise commands are sent to all attached devices. 213 Otherwise commands are sent to all attached devices.
207 wait_for_pm: If true, issues an adb wait-for-device command.
208 """ 214 """
209 215
210 def __init__(self, device=None, wait_for_pm=False): 216 def __init__(self, device=None):
211 self._adb = adb_interface.AdbInterface() 217 self._adb = adb_interface.AdbInterface()
212 if device: 218 if device:
213 self._adb.SetTargetSerial(device) 219 self._adb.SetTargetSerial(device)
214 if wait_for_pm: 220 # So many users require root that we just always do it. This could
215 self.WaitForDevicePm() 221 # be made more fine grain if necessary.
222 self._adb.EnableAdbRoot()
216 self._logcat = None 223 self._logcat = None
217 self._original_governor = None 224 self._original_governor = None
218 self._pushed_files = [] 225 self._pushed_files = []
226 self._device_utc_offset = self.RunShellCommand('date +%z')[0]
219 227
220 def Adb(self): 228 def Adb(self):
221 """Returns our AdbInterface to avoid us wrapping all its methods.""" 229 """Returns our AdbInterface to avoid us wrapping all its methods."""
222 return self._adb 230 return self._adb
223 231
232 def GetDeviceYear(self):
233 """Returns the year information of the date on device"""
234 return self.RunShellCommand('date +%Y')[0]
235
224 def WaitForDevicePm(self): 236 def WaitForDevicePm(self):
225 """Blocks until the device's package manager is available. 237 """Blocks until the device's package manager is available.
226 238
227 To workaround http://b/5201039, we restart the shell and retry if the 239 To workaround http://b/5201039, we restart the shell and retry if the
228 package manager isn't back after 120 seconds. 240 package manager isn't back after 120 seconds.
229 241
230 Raises: 242 Raises:
231 errors.WaitForResponseTimedOutError after max retries reached. 243 errors.WaitForResponseTimedOutError after max retries reached.
232 """ 244 """
233 last_err = None 245 last_err = None
(...skipping 28 matching lines...) Expand all
262 # connection; work out if we can handle this better 274 # connection; work out if we can handle this better
263 if os.environ.get('USING_HIVE'): 275 if os.environ.get('USING_HIVE'):
264 logging.warning('Ignoring reboot request as we are on hive') 276 logging.warning('Ignoring reboot request as we are on hive')
265 return 277 return
266 if full_reboot: 278 if full_reboot:
267 self._adb.SendCommand('reboot') 279 self._adb.SendCommand('reboot')
268 else: 280 else:
269 self.RestartShell() 281 self.RestartShell()
270 self.WaitForDevicePm() 282 self.WaitForDevicePm()
271 self.StartMonitoringLogcat(timeout=120) 283 self.StartMonitoringLogcat(timeout=120)
272 self.WaitForLogMatch(BOOT_COMPLETE_RE) 284 self.WaitForLogMatch(BOOT_COMPLETE_RE, None)
273 self.UnlockDevice()
274 285
275 def Uninstall(self, package): 286 def Uninstall(self, package):
276 """Uninstalls the specified package from the device. 287 """Uninstalls the specified package from the device.
277 288
278 Args: 289 Args:
279 package: Name of the package to remove. 290 package: Name of the package to remove.
291
292 Returns:
293 A status string returned by adb uninstall
280 """ 294 """
281 uninstall_command = 'uninstall %s' % package 295 uninstall_command = 'uninstall %s' % package
282 296
283 logging.info('>>> $' + uninstall_command) 297 logging.info('>>> $' + uninstall_command)
284 self._adb.SendCommand(uninstall_command, timeout_time=60) 298 return self._adb.SendCommand(uninstall_command, timeout_time=60)
285 299
286 def Install(self, package_file_path): 300 def Install(self, package_file_path):
287 """Installs the specified package to the device. 301 """Installs the specified package to the device.
288 302
289 Args: 303 Args:
290 package_file_path: Path to .apk file to install. 304 package_file_path: Path to .apk file to install.
305
306 Returns:
307 A status string returned by adb install
291 """ 308 """
292
293 assert os.path.isfile(package_file_path) 309 assert os.path.isfile(package_file_path)
294 310
295 install_command = 'install %s' % package_file_path 311 install_command = 'install %s' % package_file_path
296 312
297 logging.info('>>> $' + install_command) 313 logging.info('>>> $' + install_command)
298 self._adb.SendCommand(install_command, timeout_time=2*60) 314 return self._adb.SendCommand(install_command, timeout_time=2*60)
315
316 def MakeSystemFolderWritable(self):
317 """Remounts the /system folder rw. """
318 out = self._adb.SendCommand('remount')
319 if out.strip() != 'remount succeeded':
320 raise errors.MsgException('Remount failed: %s' % out)
299 321
300 # It is tempting to turn this function into a generator, however this is not 322 # It is tempting to turn this function into a generator, however this is not
301 # possible without using a private (local) adb_shell instance (to ensure no 323 # possible without using a private (local) adb_shell instance (to ensure no
302 # other command interleaves usage of it), which would defeat the main aim of 324 # other command interleaves usage of it), which would defeat the main aim of
303 # being able to reuse the adb shell instance across commands. 325 # being able to reuse the adb shell instance across commands.
304 def RunShellCommand(self, command, timeout_time=20, log_result=True): 326 def RunShellCommand(self, command, timeout_time=20, log_result=True):
305 """Send a command to the adb shell and return the result. 327 """Send a command to the adb shell and return the result.
306 328
307 Args: 329 Args:
308 command: String containing the shell command to send. Must not include 330 command: String containing the shell command to send. Must not include
(...skipping 21 matching lines...) Expand all
330 process: name of the process to kill off 352 process: name of the process to kill off
331 353
332 Returns: 354 Returns:
333 the number of processess killed 355 the number of processess killed
334 """ 356 """
335 pids = self.ExtractPid(process) 357 pids = self.ExtractPid(process)
336 if pids: 358 if pids:
337 self.RunShellCommand('kill ' + ' '.join(pids)) 359 self.RunShellCommand('kill ' + ' '.join(pids))
338 return len(pids) 360 return len(pids)
339 361
340 def StartActivity(self, package, activity, 362 def StartActivity(self, package, activity, wait_for_completion=False,
341 action='android.intent.action.VIEW', data=None, 363 action='android.intent.action.VIEW',
364 category=None, data=None,
342 extras=None, trace_file_name=None): 365 extras=None, trace_file_name=None):
343 """Starts |package|'s activity on the device. 366 """Starts |package|'s activity on the device.
344 367
345 Args: 368 Args:
346 package: Name of package to start (e.g. 'com.android.chrome'). 369 package: Name of package to start (e.g. 'com.google.android.apps.chrome').
347 activity: Name of activity (e.g. '.Main' or 'com.android.chrome.Main'). 370 activity: Name of activity (e.g. '.Main' or
371 'com.google.android.apps.chrome.Main').
372 wait_for_completion: wait for the activity to finish launching (-W flag).
373 action: string (e.g. "android.intent.action.MAIN"). Default is VIEW.
374 category: string (e.g. "android.intent.category.HOME")
348 data: Data string to pass to activity (e.g. 'http://www.example.com/'). 375 data: Data string to pass to activity (e.g. 'http://www.example.com/').
349 extras: Dict of extras to pass to activity. 376 extras: Dict of extras to pass to activity. Values are significant.
350 trace_file_name: If used, turns on and saves the trace to this file name. 377 trace_file_name: If used, turns on and saves the trace to this file name.
351 """ 378 """
352 cmd = 'am start -a %s -n %s/%s' % (action, package, activity) 379 cmd = 'am start -a %s' % action
380 if wait_for_completion:
381 cmd += ' -W'
382 if category:
383 cmd += ' -c %s' % category
384 if package and activity:
385 cmd += ' -n %s/%s' % (package, activity)
353 if data: 386 if data:
354 cmd += ' -d "%s"' % data 387 cmd += ' -d "%s"' % data
355 if extras: 388 if extras:
356 cmd += ' -e'
357 for key in extras: 389 for key in extras:
358 cmd += ' %s %s' % (key, extras[key]) 390 value = extras[key]
391 if isinstance(value, str):
392 cmd += ' --es'
393 elif isinstance(value, bool):
394 cmd += ' --ez'
395 elif isinstance(value, int):
396 cmd += ' --ei'
397 else:
398 raise NotImplementedError(
399 'Need to teach StartActivity how to pass %s extras' % type(value))
400 cmd += ' %s %s' % (key, value)
359 if trace_file_name: 401 if trace_file_name:
360 cmd += ' -S -P ' + trace_file_name 402 cmd += ' --start-profiler ' + trace_file_name
361 self.RunShellCommand(cmd) 403 self.RunShellCommand(cmd)
362 404
363 def EnableAdbRoot(self):
364 """Enable root on the device."""
365 self._adb.EnableAdbRoot()
366 405
367 def CloseApplication(self, package): 406 def CloseApplication(self, package):
368 """Attempt to close down the application, using increasing violence. 407 """Attempt to close down the application, using increasing violence.
369 408
370 Args: 409 Args:
371 package: Name of the process to kill off, e.g. com.android.chrome 410 package: Name of the process to kill off, e.g.
411 com.google.android.apps.chrome
372 """ 412 """
373 self.RunShellCommand('am force-stop ' + package) 413 self.RunShellCommand('am force-stop ' + package)
374 414
375 def ClearApplicationState(self, package): 415 def ClearApplicationState(self, package):
376 """Closes and clears all state for the given |package|.""" 416 """Closes and clears all state for the given |package|."""
377 self.CloseApplication(package) 417 self.CloseApplication(package)
418 self.RunShellCommand('rm -r /data/data/%s/app_*' % package)
378 self.RunShellCommand('rm -r /data/data/%s/cache/*' % package) 419 self.RunShellCommand('rm -r /data/data/%s/cache/*' % package)
379 self.RunShellCommand('rm -r /data/data/%s/files/*' % package) 420 self.RunShellCommand('rm -r /data/data/%s/files/*' % package)
380 self.RunShellCommand('rm -r /data/data/%s/shared_prefs/*' % package) 421 self.RunShellCommand('rm -r /data/data/%s/shared_prefs/*' % package)
381 422
382 def SendKeyEvent(self, keycode): 423 def SendKeyEvent(self, keycode):
383 """Sends keycode to the device. 424 """Sends keycode to the device.
384 425
385 Args: 426 Args:
386 keycode: Numeric keycode to send (see "enum" at top of file). 427 keycode: Numeric keycode to send (see "enum" at top of file).
387 """ 428 """
(...skipping 28 matching lines...) Expand all
416 # They don't match, so remove everything first and then create it. 457 # They don't match, so remove everything first and then create it.
417 if os.path.isdir(local_path): 458 if os.path.isdir(local_path):
418 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2*60) 459 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2*60)
419 self.RunShellCommand('mkdir -p %s' % device_path) 460 self.RunShellCommand('mkdir -p %s' % device_path)
420 461
421 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of 462 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of
422 # 60 seconds which isn't sufficient for a lot of users of this method. 463 # 60 seconds which isn't sufficient for a lot of users of this method.
423 push_command = 'push %s %s' % (local_path, device_path) 464 push_command = 'push %s %s' % (local_path, device_path)
424 logging.info('>>> $' + push_command) 465 logging.info('>>> $' + push_command)
425 output = self._adb.SendCommand(push_command, timeout_time=30*60) 466 output = self._adb.SendCommand(push_command, timeout_time=30*60)
467 assert output
426 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" 468 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)"
427 # Errors look like this: "failed to copy ... " 469 # Errors look like this: "failed to copy ... "
428 if not re.search('^[0-9]', output): 470 if not re.search('^[0-9]', output.splitlines()[-1]):
429 logging.critical('PUSH FAILED: ' + output) 471 logging.critical('PUSH FAILED: ' + output)
430 472
431 def GetFileContents(self, filename): 473 def GetFileContents(self, filename, log_result=True):
432 """Gets contents from the file specified by |filename|.""" 474 """Gets contents from the file specified by |filename|."""
433 return self.RunShellCommand('if [ -f "' + filename + '" ]; then cat "' + 475 return self.RunShellCommand('if [ -f "' + filename + '" ]; then cat "' +
434 filename + '"; fi') 476 filename + '"; fi', log_result=log_result)
435 477
436 def SetFileContents(self, filename, contents): 478 def SetFileContents(self, filename, contents):
437 """Writes |contents| to the file specified by |filename|.""" 479 """Writes |contents| to the file specified by |filename|."""
438 with tempfile.NamedTemporaryFile() as f: 480 with tempfile.NamedTemporaryFile() as f:
439 f.write(contents) 481 f.write(contents)
440 f.flush() 482 f.flush()
441 self._adb.Push(f.name, filename) 483 self._adb.Push(f.name, filename)
442 484
443 def RemovePushedFiles(self): 485 def RemovePushedFiles(self):
444 """Removes all files pushed with PushIfNeeded() from the device.""" 486 """Removes all files pushed with PushIfNeeded() from the device."""
(...skipping 14 matching lines...) Expand all
459 # -rw-r----- 1 user group 102 2011-05-12 12:29:54.131623387 +0100 baz.txt 501 # -rw-r----- 1 user group 102 2011-05-12 12:29:54.131623387 +0100 baz.txt
460 re_file = re.compile('^-(?P<perms>[^\s]+)\s+' 502 re_file = re.compile('^-(?P<perms>[^\s]+)\s+'
461 '(?P<user>[^\s]+)\s+' 503 '(?P<user>[^\s]+)\s+'
462 '(?P<group>[^\s]+)\s+' 504 '(?P<group>[^\s]+)\s+'
463 '(?P<size>[^\s]+)\s+' 505 '(?P<size>[^\s]+)\s+'
464 '(?P<date>[^\s]+)\s+' 506 '(?P<date>[^\s]+)\s+'
465 '(?P<time>[^\s]+)\s+' 507 '(?P<time>[^\s]+)\s+'
466 '(?P<filename>[^\s]+)$') 508 '(?P<filename>[^\s]+)$')
467 return _GetFilesFromRecursiveLsOutput( 509 return _GetFilesFromRecursiveLsOutput(
468 path, self.RunShellCommand('ls -lR %s' % path), re_file, 510 path, self.RunShellCommand('ls -lR %s' % path), re_file,
469 self.RunShellCommand('date +%z')[0]) 511 self._device_utc_offset)
470 512
471 def SetupPerformanceTest(self): 513 def SetupPerformanceTest(self):
472 """Sets up performance tests.""" 514 """Sets up performance tests."""
473 # Disable CPU scaling to reduce noise in tests 515 # Disable CPU scaling to reduce noise in tests
474 if not self._original_governor: 516 if not self._original_governor:
475 self._original_governor = self.RunShellCommand('cat ' + SCALING_GOVERNOR) 517 self._original_governor = self.GetFileContents(
518 SCALING_GOVERNOR, log_result=False)
476 self.RunShellCommand('echo performance > ' + SCALING_GOVERNOR) 519 self.RunShellCommand('echo performance > ' + SCALING_GOVERNOR)
477 self.DropRamCaches() 520 self.DropRamCaches()
478 521
479 def TearDownPerformanceTest(self): 522 def TearDownPerformanceTest(self):
480 """Tears down performance tests.""" 523 """Tears down performance tests."""
481 if self._original_governor: 524 if self._original_governor:
482 self.RunShellCommand('echo %s > %s' % (self._original_governor[0], 525 self.RunShellCommand('echo %s > %s' % (self._original_governor[0],
483 SCALING_GOVERNOR)) 526 SCALING_GOVERNOR))
484 self._original_governor = None 527 self._original_governor = None
485 528
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 """Starts monitoring the output of logcat, for use with WaitForLogMatch. 571 """Starts monitoring the output of logcat, for use with WaitForLogMatch.
529 572
530 Args: 573 Args:
531 clear: If True the existing logcat output will be cleared, to avoiding 574 clear: If True the existing logcat output will be cleared, to avoiding
532 matching historical output lurking in the log. 575 matching historical output lurking in the log.
533 timeout: How long WaitForLogMatch will wait for the given match 576 timeout: How long WaitForLogMatch will wait for the given match
534 filters: A list of logcat filters to be used. 577 filters: A list of logcat filters to be used.
535 """ 578 """
536 if clear: 579 if clear:
537 self.RunShellCommand('logcat -c') 580 self.RunShellCommand('logcat -c')
538 args = ['logcat', '-v', 'threadtime'] 581 args = []
582 if self._adb._target_arg:
583 args += shlex.split(self._adb._target_arg)
584 args += ['logcat', '-v', 'threadtime']
539 if filters: 585 if filters:
540 args.extend(filters) 586 args.extend(filters)
541 else: 587 else:
542 args.append('*:v') 588 args.append('*:v')
543 589
544 # Spawn logcat and syncronize with it. 590 # Spawn logcat and syncronize with it.
545 for _ in range(4): 591 for _ in range(4):
546 self._logcat = pexpect.spawn('adb', args, timeout=timeout, 592 self._logcat = pexpect.spawn('adb', args, timeout=timeout,
547 logfile=logfile) 593 logfile=logfile)
548 self.RunShellCommand('log startup_sync') 594 self.RunShellCommand('log startup_sync')
549 if self._logcat.expect(['startup_sync', pexpect.EOF, 595 if self._logcat.expect(['startup_sync', pexpect.EOF,
550 pexpect.TIMEOUT]) == 0: 596 pexpect.TIMEOUT]) == 0:
551 break 597 break
552 self._logcat.close(force=True) 598 self._logcat.close(force=True)
553 else: 599 else:
554 logging.critical('Error reading from logcat: ' + str(self._logcat.match)) 600 logging.critical('Error reading from logcat: ' + str(self._logcat.match))
555 sys.exit(1) 601 sys.exit(1)
556 602
557 def GetMonitoredLogCat(self): 603 def GetMonitoredLogCat(self):
558 """Returns an "adb logcat" command as created by pexpected.spawn.""" 604 """Returns an "adb logcat" command as created by pexpected.spawn."""
559 if not self._logcat: 605 if not self._logcat:
560 self.StartMonitoringLogcat(clear=False) 606 self.StartMonitoringLogcat(clear=False)
561 return self._logcat 607 return self._logcat
562 608
563 def WaitForLogMatch(self, search_re): 609 def WaitForLogMatch(self, success_re, error_re, clear=False):
564 """Blocks until a line containing |line_re| is logged or a timeout occurs. 610 """Blocks until a matching line is logged or a timeout occurs.
565 611
566 Args: 612 Args:
567 search_re: The compiled re to search each line for. 613 success_re: A compiled re to search each line for.
614 error_re: A compiled re which, if found, terminates the search for
615 |success_re|. If None is given, no error condition will be detected.
616 clear: If True the existing logcat output will be cleared, defaults to
617 false.
618
619 Raises:
620 pexpect.TIMEOUT upon the timeout specified by StartMonitoringLogcat().
568 621
569 Returns: 622 Returns:
570 The re match object. 623 The re match object if |success_re| is matched first or None if |error_re|
624 is matched first.
571 """ 625 """
572 if not self._logcat: 626 if not self._logcat:
573 self.StartMonitoringLogcat(clear=False) 627 self.StartMonitoringLogcat(clear)
574 logging.info('<<< Waiting for logcat:' + str(search_re.pattern)) 628 logging.info('<<< Waiting for logcat:' + str(success_re.pattern))
575 t0 = time.time() 629 t0 = time.time()
576 try: 630 try:
577 while True: 631 while True:
578 # Note this will block for upto the timeout _per log line_, so we need 632 # Note this will block for upto the timeout _per log line_, so we need
579 # to calculate the overall timeout remaining since t0. 633 # to calculate the overall timeout remaining since t0.
580 time_remaining = t0 + self._logcat.timeout - time.time() 634 time_remaining = t0 + self._logcat.timeout - time.time()
581 if time_remaining < 0: raise pexpect.TIMEOUT(self._logcat) 635 if time_remaining < 0: raise pexpect.TIMEOUT(self._logcat)
582 self._logcat.expect(PEXPECT_LINE_RE, timeout=time_remaining) 636 self._logcat.expect(PEXPECT_LINE_RE, timeout=time_remaining)
583 line = self._logcat.match.group(1) 637 line = self._logcat.match.group(1)
584 search_match = search_re.search(line) 638 if error_re:
585 if search_match: 639 error_match = error_re.search(line)
586 return search_match 640 if error_match:
641 return None
642 success_match = success_re.search(line)
643 if success_match:
644 return success_match
587 logging.info('<<< Skipped Logcat Line:' + str(line)) 645 logging.info('<<< Skipped Logcat Line:' + str(line))
588 except pexpect.TIMEOUT: 646 except pexpect.TIMEOUT:
589 raise pexpect.TIMEOUT( 647 raise pexpect.TIMEOUT(
590 'Timeout (%ds) exceeded waiting for pattern "%s" (tip: use -vv ' 648 'Timeout (%ds) exceeded waiting for pattern "%s" (tip: use -vv '
591 'to debug)' % 649 'to debug)' %
592 (self._logcat.timeout, search_re.pattern)) 650 (self._logcat.timeout, success_re.pattern))
593 651
594 def StartRecordingLogcat(self, clear=True, filters=['*:v']): 652 def StartRecordingLogcat(self, clear=True, filters=['*:v']):
595 """Starts recording logcat output to eventually be saved as a string. 653 """Starts recording logcat output to eventually be saved as a string.
596 654
597 This call should come before some series of tests are run, with either 655 This call should come before some series of tests are run, with either
598 StopRecordingLogcat or SearchLogcatRecord following the tests. 656 StopRecordingLogcat or SearchLogcatRecord following the tests.
599 657
600 Args: 658 Args:
601 clear: True if existing log output should be cleared. 659 clear: True if existing log output should be cleared.
602 filters: A list of logcat filters to be used. 660 filters: A list of logcat filters to be used.
603 """ 661 """
604 if clear: 662 if clear:
605 self._adb.SendCommand('logcat -c') 663 self._adb.SendCommand('logcat -c')
606 logcat_command = 'adb logcat -v threadtime %s' % ' '.join(filters) 664 logcat_command = 'adb %s logcat -v threadtime %s' % (self._adb._target_arg,
665 ' '.join(filters))
607 self.logcat_process = subprocess.Popen(logcat_command, shell=True, 666 self.logcat_process = subprocess.Popen(logcat_command, shell=True,
608 stdout=subprocess.PIPE) 667 stdout=subprocess.PIPE)
609 668
610 def StopRecordingLogcat(self): 669 def StopRecordingLogcat(self):
611 """Stops an existing logcat recording subprocess and returns output. 670 """Stops an existing logcat recording subprocess and returns output.
612 671
613 Returns: 672 Returns:
614 The logcat output as a string or an empty string if logcat was not 673 The logcat output as a string or an empty string if logcat was not
615 being recorded at the time. 674 being recorded at the time.
616 """ 675 """
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
665 return results 724 return results
666 725
667 def ExtractPid(self, process_name): 726 def ExtractPid(self, process_name):
668 """Extracts Process Ids for a given process name from Android Shell. 727 """Extracts Process Ids for a given process name from Android Shell.
669 728
670 Args: 729 Args:
671 process_name: name of the process on the device. 730 process_name: name of the process on the device.
672 731
673 Returns: 732 Returns:
674 List of all the process ids (as strings) that match the given name. 733 List of all the process ids (as strings) that match the given name.
734 If the name of a process exactly matches the given name, the pid of
735 that process will be inserted to the front of the pid list.
675 """ 736 """
676 pids = [] 737 pids = []
677 for line in self.RunShellCommand('ps'): 738 for line in self.RunShellCommand('ps', log_result=False):
678 data = line.split() 739 data = line.split()
679 try: 740 try:
680 if process_name in data[-1]: # name is in the last column 741 if process_name in data[-1]: # name is in the last column
681 pids.append(data[1]) # PID is in the second column 742 if process_name == data[-1]:
743 pids.insert(0, data[1]) # PID is in the second column
744 else:
745 pids.append(data[1])
682 except IndexError: 746 except IndexError:
683 pass 747 pass
684 return pids 748 return pids
685 749
686 def GetIoStats(self): 750 def GetIoStats(self):
687 """Gets cumulative disk IO stats since boot (for all processes). 751 """Gets cumulative disk IO stats since boot (for all processes).
688 752
689 Returns: 753 Returns:
690 Dict of {num_reads, num_writes, read_ms, write_ms} or None if there 754 Dict of {num_reads, num_writes, read_ms, write_ms} or None if there
691 was an error. 755 was an error.
692 """ 756 """
693 # Field definitions. 757 for line in self.GetFileContents('/proc/diskstats', log_result=False):
694 # http://www.kernel.org/doc/Documentation/iostats.txt 758 stats = io_stats_parser.ParseIoStatsLine(line)
695 device = 2 759 if stats.device == 'mmcblk0':
696 num_reads_issued_idx = 3
697 num_reads_merged_idx = 4
698 num_sectors_read_idx = 5
699 ms_spent_reading_idx = 6
700 num_writes_completed_idx = 7
701 num_writes_merged_idx = 8
702 num_sectors_written_idx = 9
703 ms_spent_writing_idx = 10
704 num_ios_in_progress_idx = 11
705 ms_spent_doing_io_idx = 12
706 ms_spent_doing_io_weighted_idx = 13
707
708 for line in self.RunShellCommand('cat /proc/diskstats'):
709 fields = line.split()
710 if fields[device] == 'mmcblk0':
711 return { 760 return {
712 'num_reads': int(fields[num_reads_issued_idx]), 761 'num_reads': stats.num_reads_issued,
713 'num_writes': int(fields[num_writes_completed_idx]), 762 'num_writes': stats.num_writes_completed,
714 'read_ms': int(fields[ms_spent_reading_idx]), 763 'read_ms': stats.ms_spent_reading,
715 'write_ms': int(fields[ms_spent_writing_idx]), 764 'write_ms': stats.ms_spent_writing,
716 } 765 }
717 logging.warning('Could not find disk IO stats.') 766 logging.warning('Could not find disk IO stats.')
718 return None 767 return None
719 768
720 def GetMemoryUsage(self, package): 769 def GetMemoryUsageForPid(self, pid):
770 """Returns the memory usage for given pid.
771
772 Args:
773 pid: The pid number of the specific process running on device.
774
775 Returns:
776 A tuple containg:
777 [0]: Dict of {metric:usage_kb}, for the process which has specified pid.
778 The metric keys which may be included are: Size, Rss, Pss, Shared_Clean,
779 Shared_Dirty, Private_Clean, Private_Dirty, Referenced, Swap,
780 KernelPageSize, MMUPageSize, Nvidia (tablet only).
781 [1]: Detailed /proc/[PID]/smaps information.
782 """
783 usage_dict = collections.defaultdict(int)
784 smaps = collections.defaultdict(dict)
785 current_smap = ''
786 for line in self.GetFileContents('/proc/%s/smaps' % pid, log_result=False):
787 items = line.split()
788 # See man 5 proc for more details. The format is:
789 # address perms offset dev inode pathname
790 if len(items) > 5:
791 current_smap = ' '.join(items[5:])
792 elif len(items) > 3:
793 current_smap = ' '.join(items[3:])
794 match = re.match(MEMORY_INFO_RE, line)
795 if match:
796 key = match.group('key')
797 usage_kb = int(match.group('usage_kb'))
798 usage_dict[key] += usage_kb
799 if key not in smaps[current_smap]:
800 smaps[current_smap][key] = 0
801 smaps[current_smap][key] += usage_kb
802 if not usage_dict or not any(usage_dict.values()):
803 # Presumably the process died between ps and calling this method.
804 logging.warning('Could not find memory usage for pid ' + str(pid))
805
806 for line in self.GetFileContents('/d/nvmap/generic-0/clients',
807 log_result=False):
808 match = re.match(NVIDIA_MEMORY_INFO_RE, line)
809 if match and match.group('pid') == pid:
810 usage_bytes = int(match.group('usage_bytes'))
811 usage_dict['Nvidia'] = int(round(usage_bytes / 1000.0)) # kB
812 break
813
814 return (usage_dict, smaps)
815
816 def GetMemoryUsageForPackage(self, package):
721 """Returns the memory usage for all processes whose name contains |pacakge|. 817 """Returns the memory usage for all processes whose name contains |pacakge|.
722 818
723 Args: 819 Args:
724 name: A string holding process name to lookup pid list for. 820 name: A string holding process name to lookup pid list for.
725 821
726 Returns: 822 Returns:
727 Dict of {metric:usage_kb}, summed over all pids associated with |name|. 823 A tuple containg:
728 The metric keys retruned are: Size, Rss, Pss, Shared_Clean, Shared_Dirty, 824 [0]: Dict of {metric:usage_kb}, summed over all pids associated with
729 Private_Clean, Private_Dirty, Referenced, Swap, KernelPageSize, 825 |name|.
730 MMUPageSize. 826 The metric keys which may be included are: Size, Rss, Pss, Shared_Clean,
827 Shared_Dirty, Private_Clean, Private_Dirty, Referenced, Swap,
828 KernelPageSize, MMUPageSize, Nvidia (tablet only).
829 [1]: a list with detailed /proc/[PID]/smaps information.
731 """ 830 """
732 usage_dict = collections.defaultdict(int) 831 usage_dict = collections.defaultdict(int)
733 pid_list = self.ExtractPid(package) 832 pid_list = self.ExtractPid(package)
734 # We used to use the showmap command, but it is currently broken on 833 smaps = collections.defaultdict(dict)
735 # stingray so it's easier to just parse /proc/<pid>/smaps directly. 834
736 memory_stat_re = re.compile('^(?P<key>\w+):\s+(?P<value>\d+) kB$')
737 for pid in pid_list: 835 for pid in pid_list:
738 for line in self.RunShellCommand('cat /proc/%s/smaps' % pid, 836 usage_dict_per_pid, smaps_per_pid = self.GetMemoryUsageForPid(pid)
739 log_result=False): 837 smaps[pid] = smaps_per_pid
740 match = re.match(memory_stat_re, line) 838 for (key, value) in usage_dict_per_pid.items():
741 if match: usage_dict[match.group('key')] += int(match.group('value')) 839 usage_dict[key] += value
742 if not usage_dict or not any(usage_dict.values()):
743 # Presumably the process died between ps and showmap.
744 logging.warning('Could not find memory usage for pid ' + str(pid))
745 return usage_dict
746 840
747 def UnlockDevice(self): 841 return usage_dict, smaps
748 """Unlocks the screen of the device."""
749 # Make sure a menu button event will actually unlock the screen.
750 if IsRunningAsBuildbot():
751 assert self.RunShellCommand('getprop ro.test_harness')[0].strip() == '1'
752 # The following keyevent unlocks the screen if locked.
753 self.SendKeyEvent(KEYCODE_MENU)
754 # If the screen wasn't locked the previous command will bring up the menu,
755 # which this will dismiss. Otherwise this shouldn't change anything.
756 self.SendKeyEvent(KEYCODE_BACK)
OLDNEW
« no previous file with comments | « build/android/lighttpd_server.py ('k') | build/android/pylib/base_test_runner.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698