1 #!/usr/bin/env python 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 import os 7 import sys 8 import time 9 10 import pyauto_functional # Must be imported before pyauto 11 import pyauto 12 import test_utils 13 14 15 class MemoryTest(pyauto.PyUITest): 16 """Tests for memory usage of Chrome-related processes. 17 18 These tests are meant to be used manually, not as part of the continuous 19 test cycle. This is because each test starts up and periodically 20 measures/records the memory usage of a relevant Chrome process, doing so 21 repeatedly until the test is manually killed. Currently, this script only 22 works in Linux and ChromeOS, as it uses a Linux shell command to query the 23 system for process memory usage info (test_utils.GetMemoryUsageOfProcess()). 24 25 The tests in this suite produce the following output files (relative to the 26 current working directory): 27 28 testTabRendererProcessMemoryUsage: 'renderer_process_mem.txt' 29 testExtensionProcessMemoryUsage: 'extension_process_mem.txt' 30 """ 31 32 # Constants for all tests in this suite. 33 NUM_SECONDS_BETWEEN_MEASUREMENTS = 10 34 MEASUREMENT_LOG_MESSAGE_TEMPLATE = '[%s] %.2f MB (pid: %d)' 35 LOG_TO_OUTPUT_FILE = True 36 37 # Constants for testTabRendererProcessMemoryUsage. 38 RENDERER_PROCESS_URL = 'http://chrome.angrybirds.com' 39 RENDERER_PROCESS_OUTPUT_FILE = 'renderer_process_mem.txt' 40 41 # Constants for testExtensionProcessMemoryUsage. 42 EXTENSION_LOCATION = os.path.abspath(os.path.join( 43 pyauto.PyUITest.DataDir(), 'extensions', 'google_talk.crx')) 44 EXTENSION_PROCESS_NAME = 'Google Talk' 45 EXTENSION_PROCESS_OUTPUT_FILE = 'extension_process_mem.txt' 46 47 def _GetPidOfExtensionProcessByName(self, name): 48 """Identifies the process ID of an extension process, given its name. 49 50 Args: 51 name: The string name of an extension process, as returned by the function 52 GetBrowserInfo(). 53 54 Returns: 55 The integer process identifier (PID) for the specified process, or 56 None if the PID cannot be identified. 57 """ 58 info = self.GetBrowserInfo()['extension_views'] 59 pid = [x['pid'] for x in info if x['name'] == '%s' % name] 60 if pid: 61 return pid[0] 62 return None 63 64 def _LogMessage(self, log_file, msg): 65 """Logs a message to the screen, and to a log file if necessary. 66 67 Args: 68 log_file: The string name of a log file to which to write. 69 msg: The message to log. 70 """ 71 print msg 72 sys.stdout.flush() 73 if self.LOG_TO_OUTPUT_FILE: 74 print >>open(log_file, 'a'), msg 75 76 def testTabRendererProcessMemoryUsage(self): 77 """Test the memory usage of the renderer process for a tab. 78 79 This test periodically queries the system for the current memory usage 80 of a tab's renderer process. The test will take measurements forever; you 81 must manually kill the test to terminate it. 82 """ 83 if (self.LOG_TO_OUTPUT_FILE and 84 os.path.exists(self.RENDERER_PROCESS_OUTPUT_FILE)): 85 os.remove(self.RENDERER_PROCESS_OUTPUT_FILE) 86 self.NavigateToURL(self.RENDERER_PROCESS_URL) 87 self._LogMessage( 88 self.RENDERER_PROCESS_OUTPUT_FILE, 89 'Memory usage for renderer process of a tab navigated to: "%s"' % ( 90 self.RENDERER_PROCESS_URL)) 91 92 # A user must manually kill this test to terminate the following loop. 93 while True: 94 pid = self.GetBrowserInfo()['windows'][0]['tabs'][0]['renderer_pid'] 95 usage = test_utils.GetMemoryUsageOfProcess(pid) 96 current_time = time.asctime(time.localtime(time.time())) 97 self._LogMessage( 98 self.RENDERER_PROCESS_OUTPUT_FILE, 99 self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid)) 100 time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS) 101 102 def testExtensionProcessMemoryUsage(self): 103 """Test the memory usage of an extension process. 104 105 This test periodically queries the system for the current memory usage 106 of an extension process. The test will take measurements forever; you 107 must manually kill the test to terminate it. 108 """ 109 if (self.LOG_TO_OUTPUT_FILE and 110 os.path.exists(self.EXTENSION_PROCESS_OUTPUT_FILE)): 111 os.remove(self.EXTENSION_PROCESS_OUTPUT_FILE) 112 self.InstallExtension(self.EXTENSION_LOCATION) 113 # The PID is 0 until the extension has a chance to start up. 114 self.WaitUntil( 115 lambda: self._GetPidOfExtensionProcessByName( 116 self.EXTENSION_PROCESS_NAME) not in [0, None]) 117 self._LogMessage( 118 self.EXTENSION_PROCESS_OUTPUT_FILE, 119 'Memory usage for extension process with name: "%s"' % ( 120 self.EXTENSION_PROCESS_NAME)) 121 122 # A user must manually kill this test to terminate the following loop. 123 while True: 124 pid = self._GetPidOfExtensionProcessByName(self.EXTENSION_PROCESS_NAME) 125 usage = test_utils.GetMemoryUsageOfProcess(pid) 126 current_time = time.asctime(time.localtime(time.time())) 127 self._LogMessage( 128 self.EXTENSION_PROCESS_OUTPUT_FILE, 129 self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid)) 130 time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS) 131 132 133 if __name__ == '__main__': 134 pyauto_functional.Main() 135