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

Side by Side Diff: chrome/test/functional/perf.py

Issue 10161033: Adding pyauto-based memory usage tests for ChromeOS. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Minor change to a comment. Created 8 years, 7 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Basic pyauto performance tests. 6 """Basic pyauto performance tests.
7 7
8 For tests that need to be run for multiple iterations (e.g., so that average 8 For tests that need to be run for multiple iterations (e.g., so that average
9 and standard deviation values can be reported), the default number of iterations 9 and standard deviation values can be reported), the default number of iterations
10 run for each of these tests is specified by |_DEFAULT_NUM_ITERATIONS|. 10 run for each of these tests is specified by |_DEFAULT_NUM_ITERATIONS|.
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 self._seen_graph_lines = {} 83 self._seen_graph_lines = {}
84 84
85 pyauto.PyUITest.setUp(self) 85 pyauto.PyUITest.setUp(self)
86 86
87 # Flush all buffers to disk and wait until system calms down. Must be done 87 # Flush all buffers to disk and wait until system calms down. Must be done
88 # *after* calling pyauto.PyUITest.setUp, since that is where Chrome is 88 # *after* calling pyauto.PyUITest.setUp, since that is where Chrome is
89 # killed and re-initialized for a new test. 89 # killed and re-initialized for a new test.
90 # TODO(dennisjeffrey): Implement wait for idle CPU on Windows/Mac. 90 # TODO(dennisjeffrey): Implement wait for idle CPU on Windows/Mac.
91 if self.IsLinux(): # IsLinux() also implies IsChromeOS(). 91 if self.IsLinux(): # IsLinux() also implies IsChromeOS().
92 os.system('sync') 92 os.system('sync')
93 self._WaitForIdleCPU(60.0, 0.03) 93 self._WaitForIdleCPU(60.0, 0.05)
94 94
95 def _WaitForIdleCPU(self, timeout, utilization): 95 def _WaitForIdleCPU(self, timeout, utilization):
96 """Waits for the CPU to become idle (< utilization). 96 """Waits for the CPU to become idle (< utilization).
97 97
98 Args: 98 Args:
99 timeout: The longest time in seconds to wait before throwing an error. 99 timeout: The longest time in seconds to wait before throwing an error.
100 utilization: The CPU usage below which the system should be considered 100 utilization: The CPU usage below which the system should be considered
101 idle (between 0 and 1.0 independent of cores/hyperthreads). 101 idle (between 0 and 1.0 independent of cores/hyperthreads).
102 """ 102 """
103 time_passed = 0.0 103 time_passed = 0.0
(...skipping 1654 matching lines...) Expand 10 before | Expand all | Expand 10 after
1758 def testIntl2File(self): 1758 def testIntl2File(self):
1759 self._RunPageCyclerTest('intl2', self._num_iterations, 'Intl2File') 1759 self._RunPageCyclerTest('intl2', self._num_iterations, 'Intl2File')
1760 1760
1761 def testMozFile(self): 1761 def testMozFile(self):
1762 self._RunPageCyclerTest('moz', self._num_iterations, 'MozFile') 1762 self._RunPageCyclerTest('moz', self._num_iterations, 'MozFile')
1763 1763
1764 def testMoz2File(self): 1764 def testMoz2File(self):
1765 self._RunPageCyclerTest('moz2', self._num_iterations, 'Moz2File') 1765 self._RunPageCyclerTest('moz2', self._num_iterations, 'Moz2File')
1766 1766
1767 1767
1768 class MemoryTest(BasePerfTest):
1769 """Tests to measure memory consumption under different usage scenarios."""
1770
1771 def setUp(self):
1772 pyauto.PyUITest.setUp(self)
1773
1774 # Log in to get a fresh Chrome instance with a clean memory state (if
1775 # already logged in, log out first).
1776 if self.GetLoginInfo()['is_logged_in']:
1777 self.Logout()
1778 self.assertFalse(self.GetLoginInfo()['is_logged_in'],
1779 msg='Failed to log out.')
1780
1781 credentials = self.GetPrivateInfo()['test_google_account']
1782 self.Login(credentials['username'], credentials['password'])
1783 self.assertTrue(self.GetLoginInfo()['is_logged_in'],
1784 msg='Failed to log in.')
1785
1786 def ExtraChromeFlags(self):
1787 """Launches Chrome with custom flags.
1788
1789 Returns:
1790 A list of extra flags to pass to Chrome when it is launched.
1791 """
1792 # Ensure Chrome assigns one renderer process to each tab.
1793 return super(MemoryTest, self).ExtraChromeFlags() + ['--process-per-tab']
1794
1795 def _GetMemoryStats(self, duration):
1796 """Identifies and returns different kinds of current memory usage stats.
1797
1798 This function samples values each second for |duration| seconds, then
1799 outputs the min, max, and ending values for each measurement type.
1800
1801 Args:
1802 duration: The number of seconds to sample data before outputting the
1803 minimum, maximum, and ending values for each measurement type.
1804
1805 Returns:
1806 A dictionary containing memory usage information. Each measurement type
1807 is associated with the min, max, and ending values from among all
1808 sampled values. Values are specified in KB.
1809 {
1810 'gem_obj': { # GPU memory usage.
1811 'min': ...,
1812 'max': ...,
1813 'end': ...,
1814 },
1815 'gtt': { ... }, # GPU memory usage (graphics translation table).
1816 'mem_free': { ... }, # CPU free memory.
1817 'mem_available': { ... }, # CPU available memory.
1818 'mem_shared': { ... }, # CPU shared memory.
1819 'mem_cached': { ... }, # CPU cached memory.
1820 'mem_anon': { ... }, # CPU anon memory (active + inactive).
1821 'mem_file': { ... }, # CPU file memory (active + inactive).
1822 'mem_slab': { ... }, # CPU slab memory.
1823 'browser_priv': { ... }, # Chrome browser private memory.
1824 'browser_shared': { ... }, # Chrome browser shared memory.
1825 'gpu_priv': { ... }, # Chrome GPU private memory.
1826 'gpu_shared': { ... }, # Chrome GPU shared memory.
1827 'renderer_priv': { ... }, # Total private memory of all renderers.
1828 'renderer_shared': { ... }, # Total shared memory of all renderers.
1829 }
1830 """
1831 logging.debug('Sampling memory information for %d seconds...' % duration)
1832 stats = {}
1833
1834 for _ in xrange(duration):
1835 # GPU memory.
1836 gem_obj_path = '/sys/kernel/debug/dri/0/i915_gem_objects'
1837 if os.path.exists(gem_obj_path):
1838 p = subprocess.Popen('grep bytes %s' % gem_obj_path,
1839 stdout=subprocess.PIPE, shell=True)
1840 stdout = p.communicate()[0]
1841
1842 gem_obj = re.search(
1843 '\d+ objects, (\d+) bytes\n', stdout).group(1)
1844 if 'gem_obj' not in stats:
1845 stats['gem_obj'] = []
1846 stats['gem_obj'].append(int(gem_obj) / 1024.0)
1847
1848 gtt_path = '/sys/kernel/debug/dri/0/i915_gem_gtt'
1849 if os.path.exists(gtt_path):
1850 p = subprocess.Popen('grep bytes %s' % gtt_path,
1851 stdout=subprocess.PIPE, shell=True)
1852 stdout = p.communicate()[0]
1853
1854 gtt = re.search(
1855 'Total [\d]+ objects, ([\d]+) bytes', stdout).group(1)
1856 if 'gtt' not in stats:
1857 stats['gtt'] = []
1858 stats['gtt'].append(int(gtt) / 1024.0)
1859
1860 # CPU memory.
1861 stdout = ''
1862 with open('/proc/meminfo') as f:
1863 stdout = f.read()
1864 mem_free = re.search('MemFree:\s*([\d]+) kB', stdout).group(1)
1865
1866 if 'mem_free' not in stats:
1867 stats['mem_free'] = []
1868 stats['mem_free'].append(int(mem_free))
1869
1870 mem_dirty = re.search('Dirty:\s*([\d]+) kB', stdout).group(1)
1871 mem_active_file = re.search(
1872 'Active\(file\):\s*([\d]+) kB', stdout).group(1)
1873 mem_inactive_file = re.search(
1874 'Inactive\(file\):\s*([\d]+) kB', stdout).group(1)
1875
1876 with open('/proc/sys/vm/min_filelist_kbytes') as f:
1877 mem_min_file = f.read()
1878
1879 # Available memory =
1880 # MemFree + ActiveFile + InactiveFile - DirtyMem - MinFileMem
1881 if 'mem_available' not in stats:
1882 stats['mem_available'] = []
1883 stats['mem_available'].append(
1884 int(mem_free) + int(mem_active_file) + int(mem_inactive_file) -
1885 int(mem_dirty) - int(mem_min_file))
1886
1887 mem_shared = re.search('Shmem:\s*([\d]+) kB', stdout).group(1)
1888 if 'mem_shared' not in stats:
1889 stats['mem_shared'] = []
1890 stats['mem_shared'].append(int(mem_shared))
1891
1892 mem_cached = re.search('Cached:\s*([\d]+) kB', stdout).group(1)
1893 if 'mem_cached' not in stats:
1894 stats['mem_cached'] = []
1895 stats['mem_cached'].append(int(mem_cached))
1896
1897 mem_anon_active = re.search('Active\(anon\):\s*([\d]+) kB',
1898 stdout).group(1)
1899 mem_anon_inactive = re.search('Inactive\(anon\):\s*([\d]+) kB',
1900 stdout).group(1)
1901 if 'mem_anon' not in stats:
1902 stats['mem_anon'] = []
1903 stats['mem_anon'].append(int(mem_anon_active) + int(mem_anon_inactive))
1904
1905 mem_file_active = re.search('Active\(file\):\s*([\d]+) kB',
1906 stdout).group(1)
1907 mem_file_inactive = re.search('Inactive\(file\):\s*([\d]+) kB',
1908 stdout).group(1)
1909 if 'mem_file' not in stats:
1910 stats['mem_file'] = []
1911 stats['mem_file'].append(int(mem_file_active) + int(mem_file_inactive))
1912
1913 mem_slab = re.search('Slab:\s*([\d]+) kB', stdout).group(1)
1914 if 'mem_slab' not in stats:
1915 stats['mem_slab'] = []
1916 stats['mem_slab'].append(int(mem_slab))
1917
1918 # Chrome process memory.
1919 pinfo = self.GetProcessInfo()['browsers'][0]['processes']
1920 total_renderer_priv = 0
1921 total_renderer_shared = 0
1922 for process in pinfo:
1923 mem_priv = process['working_set_mem']['priv']
1924 mem_shared = process['working_set_mem']['shared']
1925 if process['child_process_type'] == 'Browser':
1926 if 'browser_priv' not in stats:
1927 stats['browser_priv'] = []
1928 stats['browser_priv'].append(int(mem_priv))
1929 if 'browser_shared' not in stats:
1930 stats['browser_shared'] = []
1931 stats['browser_shared'].append(int(mem_shared))
1932 elif process['child_process_type'] == 'GPU':
1933 if 'gpu_priv' not in stats:
1934 stats['gpu_priv'] = []
1935 stats['gpu_priv'].append(int(mem_priv))
1936 if 'gpu_shared' not in stats:
1937 stats['gpu_shared'] = []
1938 stats['gpu_shared'].append(int(mem_shared))
1939 elif process['child_process_type'] == 'Tab':
1940 # Sum the memory of all renderer processes.
1941 total_renderer_priv += int(mem_priv)
1942 total_renderer_shared += int(mem_shared)
1943 if 'renderer_priv' not in stats:
1944 stats['renderer_priv'] = []
1945 stats['renderer_priv'].append(int(total_renderer_priv))
1946 if 'renderer_shared' not in stats:
1947 stats['renderer_shared'] = []
1948 stats['renderer_shared'].append(int(total_renderer_shared))
1949
1950 time.sleep(1)
1951
1952 # Compute min, max, and ending values to return.
1953 result = {}
1954 for measurement_type in stats:
1955 values = stats[measurement_type]
1956 result[measurement_type] = {
1957 'min': min(values),
1958 'max': max(values),
1959 'end': values[-1],
1960 }
1961
1962 return result
1963
1964 def _RecordMemoryStats(self, description, when, duration):
1965 """Outputs memory statistics to be graphed.
1966
1967 Args:
1968 description: A string description for the test. Should not contain
1969 spaces. For example, 'MemCtrl'.
1970 when: A string description of when the memory stats are being recorded
1971 during test execution (since memory stats may be recorded multiple
1972 times during a test execution at certain "interesting" times). Should
1973 not contain spaces.
1974 duration: The number of seconds to sample data before outputting the
1975 memory statistics.
1976 """
1977 mem = self._GetMemoryStats(duration)
1978 measurement_types = [
1979 ('gem_obj', 'GemObj'),
1980 ('gtt', 'GTT'),
1981 ('mem_free', 'MemFree'),
1982 ('mem_available', 'MemAvail'),
1983 ('mem_shared', 'MemShare'),
1984 ('mem_cached', 'MemCache'),
1985 ('mem_anon', 'MemAnon'),
1986 ('mem_file', 'MemFile'),
1987 ('mem_slab', 'MemSlab'),
1988 ('browser_priv', 'BrowPriv'),
1989 ('browser_shared', 'BrowShar'),
1990 ('gpu_priv', 'GpuPriv'),
1991 ('gpu_shared', 'GpuShar'),
1992 ('renderer_priv', 'RendPriv'),
1993 ('renderer_shared', 'RendShar'),
1994 ]
1995 for type_key, type_string in measurement_types:
1996 if type_key not in mem:
1997 continue
1998 self._OutputPerfGraphValue(
1999 '%s-Min%s-%s' % (description, type_string, when),
2000 mem[type_key]['min'], 'KB', '%s-%s' % (description, type_string))
2001 self._OutputPerfGraphValue(
2002 '%s-Max%s-%s' % (description, type_string, when),
2003 mem[type_key]['max'], 'KB', '%s-%s' % (description, type_string))
2004 self._OutputPerfGraphValue(
2005 '%s-End%s-%s' % (description, type_string, when),
2006 mem[type_key]['end'], 'KB', '%s-%s' % (description, type_string))
2007
2008 def _RunTest(self, tabs, description, duration):
2009 """Runs a general memory test.
2010
2011 Args:
2012 tabs: A list of strings representing the URLs of the websites to open
2013 during this test.
2014 description: A string description for the test. Should not contain
2015 spaces. For example, 'MemCtrl'.
2016 duration: The number of seconds to sample data before outputting memory
2017 statistics.
2018 """
2019 self._RecordMemoryStats(description, '0Tabs0', duration)
2020
2021 for iteration_num in xrange(2):
2022 for site in tabs:
2023 self.AppendTab(pyauto.GURL(site))
2024
2025 self._RecordMemoryStats(description,
2026 '%dTabs%d' % (len(tabs), iteration_num + 1),
2027 duration)
2028
2029 for _ in xrange(len(tabs)):
2030 self.GetBrowserWindow(0).GetTab(1).Close(True)
2031
2032 self._RecordMemoryStats(description, '0Tabs%d' % (iteration_num + 1),
2033 duration)
2034
2035 def testOpenCloseTabsControl(self):
2036 """Measures memory usage when opening/closing tabs to about:blank."""
2037 tabs = ['about:blank'] * 10
2038 self._RunTest(tabs, 'MemCtrl', 15)
2039
2040 def testOpenCloseTabsLiveSites(self):
2041 """Measures memory usage when opening/closing tabs to live sites."""
2042 tabs = [
2043 'http://www.google.com/gmail',
2044 'http://www.google.com/calendar',
2045 'http://www.google.com/plus',
2046 'http://www.google.com/youtube',
2047 'http://www.nytimes.com',
2048 'http://www.cnn.com',
2049 'http://www.facebook.com',
2050 'http://www.techcrunch.com',
2051 'http://www.theverge.com',
2052 'http://www.yahoo.com',
2053 ]
2054 # Log in to a test Google account to make connections to the above Google
2055 # websites more interesting.
2056 self._LoginToGoogleAccount()
2057 self._RunTest(tabs, 'MemLive', 20)
2058
2059
1768 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 2060 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
1769 """Request handler for the local performance test server.""" 2061 """Request handler for the local performance test server."""
1770 2062
1771 def _IgnoreHandler(self, unused_args): 2063 def _IgnoreHandler(self, unused_args):
1772 """A GET request handler that simply replies with status code 200. 2064 """A GET request handler that simply replies with status code 200.
1773 2065
1774 Args: 2066 Args:
1775 unused_args: A dictionary of arguments for the current GET request. 2067 unused_args: A dictionary of arguments for the current GET request.
1776 The arguments are ignored. 2068 The arguments are ignored.
1777 """ 2069 """
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1990 """Identifies the port number to which the server is currently bound. 2282 """Identifies the port number to which the server is currently bound.
1991 2283
1992 Returns: 2284 Returns:
1993 The numeric port number to which the server is currently bound. 2285 The numeric port number to which the server is currently bound.
1994 """ 2286 """
1995 return self._server.server_address[1] 2287 return self._server.server_address[1]
1996 2288
1997 2289
1998 if __name__ == '__main__': 2290 if __name__ == '__main__':
1999 pyauto_functional.Main() 2291 pyauto_functional.Main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698