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

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: Now recording many more types of memory stats. 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
Sonny 2012/04/28 00:40:02 Forgot to put this in it's own comment last time,
dennis_jeffrey 2012/04/28 00:55:17 Done.
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 _GetMemoryStats(self, duration):
1787 """Identifies and returns different kinds of current memory usage stats.
1788
1789 This function samples values each second for |duration| seconds, then
1790 outputs the min, max, and ending values for each measurement type.
1791
1792 Args:
1793 duration: The number of seconds to sample data before outputting the
1794 minimum, maximum, and ending values for each measurement type.
1795
1796 Returns:
1797 A dictionary containing memory usage information. Each measurement type
1798 is associated with the min, max, and ending values from among all
1799 sampled values. Values are specified in KB.
1800 {
1801 'gem_obj': { # GPU memory usage.
1802 'min': ...,
1803 'max': ...,
1804 'end': ...,
1805 },
1806 'mem_free': { ...}, # CPU free memory.
1807 'mem_available': { ... }, # CPU available memory
1808 'mem_shared': { ... }, # CPU shared memory
1809 'mem_cached': { ... }, # CPU cached memory
1810 'mem_anon': { ... }, # CPU anon memory (active + inactive)
1811 'mem_file': { ... }, # CPU file memory (active + inactive)
1812 'mem_slab': { ... }, # CPU slab memory
1813 'browser_priv': { ... }, # Chrome browser private memory
1814 'browser_shared': { ... }, # Chrome browser shared memory
1815 'gpu_priv': { ... }, # Chrome GPU private memory
1816 'gpu_shared': { ... }, # Chrome GPU shared memory
1817 'renderer_priv': { ... }, # Total private memory of all renderers.
1818 'renderer_shared': { ... }, # Total shared memory of all renderers.
1819 }
1820 """
1821 logging.debug('Sampling memory information for %d seconds...' % duration)
1822 stats = {}
1823
1824 for _ in xrange(duration):
1825 gem_obj_path = '/sys/kernel/debug/dri/0/i915_gem_objects'
1826 if os.path.exists(gem_obj_path):
1827 p = subprocess.Popen('grep bytes %s' % gem_obj_path,
1828 stdout=subprocess.PIPE, shell=True)
1829 stdout = p.communicate()[0]
1830
1831 # GPU memory.
1832 gem_obj = re.search(
1833 '\d+ objects, (\d+) bytes\n', stdout).group(1)
1834 if 'gem_obj' not in stats:
1835 stats['gem_obj'] = []
1836 stats['gem_obj'].append(int(gem_obj) / 1024.0)
1837
1838 # CPU memory.
1839 stdout = ''
1840 with open('/proc/meminfo') as f:
1841 stdout = f.read()
1842 mem_free = re.search('MemFree:\s*([\d]+) kB', stdout).group(1)
1843
1844 if 'mem_free' not in stats:
1845 stats['mem_free'] = []
1846 stats['mem_free'].append(int(mem_free))
1847
1848 mem_dirty = re.search('Dirty:\s*([\d]+) kB', stdout).group(1)
1849 mem_active_file = re.search(
1850 'Active\(file\):\s*([\d]+) kB', stdout).group(1)
1851 mem_inactive_file = re.search(
1852 'Inactive\(file\):\s*([\d]+) kB', stdout).group(1)
1853
1854 with open('/proc/sys/vm/min_filelist_kbytes') as f:
1855 mem_min_file = f.read()
1856
1857 # Available memory =
1858 # MemFree + ActiveFile + InactiveFile - DirtyMem - MinFileMem
1859 if 'mem_available' not in stats:
1860 stats['mem_available'] = []
1861 stats['mem_available'].append(
1862 int(mem_free) + int(mem_active_file) + int(mem_inactive_file) -
1863 int(mem_dirty) - int(mem_min_file))
1864
1865 mem_shared = re.search('Shmem:\s*([\d]+) kB', stdout).group(1)
1866 if 'mem_shared' not in stats:
1867 stats['mem_shared'] = []
1868 stats['mem_shared'].append(int(mem_shared))
1869
1870 mem_cached = re.search('Cached:\s*([\d]+) kB', stdout).group(1)
1871 if 'mem_cached' not in stats:
1872 stats['mem_cached'] = []
1873 stats['mem_cached'].append(int(mem_cached))
1874
1875 mem_anon_active = re.search('Active\(anon\):\s*([\d]+) kB',
1876 stdout).group(1)
1877 mem_anon_inactive = re.search('Inactive\(anon\):\s*([\d]+) kB',
1878 stdout).group(1)
1879 if 'mem_anon' not in stats:
1880 stats['mem_anon'] = []
1881 stats['mem_anon'].append(int(mem_anon_active) + int(mem_anon_inactive))
1882
1883 mem_file_active = re.search('Active\(file\):\s*([\d]+) kB',
1884 stdout).group(1)
1885 mem_file_inactive = re.search('Inactive\(file\):\s*([\d]+) kB',
1886 stdout).group(1)
1887 if 'mem_file' not in stats:
1888 stats['mem_file'] = []
1889 stats['mem_file'].append(int(mem_file_active) + int(mem_file_inactive))
1890
1891 mem_slab = re.search('Slab:\s*([\d]+) kB', stdout).group(1)
1892 if 'mem_slab' not in stats:
1893 stats['mem_slab'] = []
1894 stats['mem_slab'].append(int(mem_slab))
1895
1896 # Chrome process memory.
1897 pinfo = self.GetProcessInfo()['browsers'][0]['processes']
1898 total_renderer_priv = 0
1899 total_renderer_shared = 0
1900 for process in pinfo:
1901 mem_priv = process['working_set_mem']['priv']
1902 mem_shared = process['working_set_mem']['shared']
1903 if process['child_process_type'] == 'Browser':
1904 if 'browser_priv' not in stats:
1905 stats['browser_priv'] = []
1906 stats['browser_priv'].append(int(mem_priv))
1907 if 'browser_shared' not in stats:
1908 stats['browser_shared'] = []
1909 stats['browser_shared'].append(int(mem_shared))
1910 elif process['child_process_type'] == 'GPU':
1911 if 'gpu_priv' not in stats:
1912 stats['gpu_priv'] = []
1913 stats['gpu_priv'].append(int(mem_priv))
1914 if 'gpu_shared' not in stats:
1915 stats['gpu_shared'] = []
1916 stats['gpu_shared'].append(int(mem_shared))
1917 elif process['child_process_type'] == 'Tab':
1918 # Sum the memory of all renderer processes.
1919 total_renderer_priv += int(mem_priv)
1920 total_renderer_shared += int(mem_shared)
1921 if 'renderer_priv' not in stats:
1922 stats['renderer_priv'] = []
1923 stats['renderer_priv'].append(int(total_renderer_priv))
1924 if 'renderer_shared' not in stats:
1925 stats['renderer_shared'] = []
1926 stats['renderer_shared'].append(int(total_renderer_shared))
1927
1928 time.sleep(1)
1929
1930 # Compute min, max, and ending values to return.
1931 result = {}
1932 for measurement_type in stats:
1933 values = stats[measurement_type]
1934 result[measurement_type] = {
1935 'min': min(values),
1936 'max': max(values),
1937 'end': values[-1],
1938 }
1939
1940 return result
1941
1942 def _RecordMemoryStats(self, description, when, duration):
1943 """Outputs memory statistics to be graphed.
1944
1945 Args:
1946 description: A string description for the test. Should not contain
1947 spaces. For example, 'MemCtrl'.
1948 when: A string description of when the memory stats are being recorded
1949 during test execution (since memory stats may be recorded multiple
1950 times during a test execution at certain "interesting" times). Should
1951 not contain spaces.
1952 duration: The number of seconds to sample data before outputting the
1953 memory statistics.
1954 """
1955 mem = self._GetMemoryStats(duration)
1956 measurement_types = [
1957 ('gem_obj', 'GemObj'),
1958 ('mem_free', 'MemFree'),
1959 ('mem_available', 'MemAvail'),
1960 ('mem_shared', 'MemShare'),
1961 ('mem_cached', 'MemCache'),
1962 ('mem_anon', 'MemAnon'),
1963 ('mem_file', 'MemFile'),
1964 ('mem_slab', 'MemSlab'),
1965 ('browser_priv', 'BrowPriv'),
1966 ('browser_shared', 'BrowShar'),
1967 ('gpu_priv', 'GpuPriv'),
1968 ('gpu_shared', 'GpuShar'),
1969 ('renderer_priv', 'RendPriv'),
1970 ('renderer_shared', 'RendShar'),
1971 ]
1972 for type_key, type_string in measurement_types:
1973 if type_key not in mem:
1974 continue
1975 self._OutputPerfGraphValue(
1976 '%s-Min%s-%s' % (description, type_string, when),
1977 mem[type_key]['min'], 'KB', '%s-%s' % (description, type_string))
1978 self._OutputPerfGraphValue(
1979 '%s-Max%s-%s' % (description, type_string, when),
1980 mem[type_key]['max'], 'KB', '%s-%s' % (description, type_string))
1981 self._OutputPerfGraphValue(
1982 '%s-End%s-%s' % (description, type_string, when),
1983 mem[type_key]['end'], 'KB', '%s-%s' % (description, type_string))
1984
1985 def _RunTest(self, tabs, description, duration):
1986 """Runs a general memory test.
1987
1988 Args:
1989 tabs: A list of strings representing the URLs of the websites to open
1990 during this test.
1991 description: A string description for the test. Should not contain
1992 spaces. For example, 'MemCtrl'.
1993 duration: The number of seconds to sample data before outputting memory
1994 statistics.
1995 """
1996 self._RecordMemoryStats(description, '0Tabs0', duration)
1997
1998 for iteration_num in xrange(2):
1999 for site in tabs:
2000 self.AppendTab(pyauto.GURL(site))
2001
2002 self._RecordMemoryStats(description,
2003 '%dTabs%d' % (len(tabs), iteration_num + 1),
2004 duration)
2005
2006 for _ in xrange(len(tabs)):
2007 self.GetBrowserWindow(0).GetTab(1).Close(True)
2008
2009 self._RecordMemoryStats(description, '0Tabs%d' % (iteration_num + 1),
2010 duration)
2011
2012 def testOpenCloseTabsControl(self):
2013 """Measures memory usage when opening/closing tabs to about:blank."""
2014 tabs = ['about:blank'] * 10
2015 self._RunTest(tabs, 'MemCtrl', 15)
2016
2017 def testOpenCloseTabsLiveSites(self):
2018 """Measures memory usage when opening/closing tabs to live sites."""
2019 tabs = [
2020 'http://www.google.com/gmail',
2021 'http://www.google.com/calendar',
2022 'http://www.google.com/plus',
2023 'http://www.google.com/youtube',
2024 'http://www.nytimes.com',
2025 'http://www.cnn.com',
2026 'http://www.facebook.com',
2027 'http://www.techcrunch.com',
2028 'http://www.theverge.com',
2029 'http://www.yahoo.com',
2030 ]
2031 # Log in to a test Google account to make connections to the above Google
2032 # websites more interesting.
2033 self._LoginToGoogleAccount()
2034 self._RunTest(tabs, 'MemLive', 20)
2035
2036
1768 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 2037 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
1769 """Request handler for the local performance test server.""" 2038 """Request handler for the local performance test server."""
1770 2039
1771 def _IgnoreHandler(self, unused_args): 2040 def _IgnoreHandler(self, unused_args):
1772 """A GET request handler that simply replies with status code 200. 2041 """A GET request handler that simply replies with status code 200.
1773 2042
1774 Args: 2043 Args:
1775 unused_args: A dictionary of arguments for the current GET request. 2044 unused_args: A dictionary of arguments for the current GET request.
1776 The arguments are ignored. 2045 The arguments are ignored.
1777 """ 2046 """
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1990 """Identifies the port number to which the server is currently bound. 2259 """Identifies the port number to which the server is currently bound.
1991 2260
1992 Returns: 2261 Returns:
1993 The numeric port number to which the server is currently bound. 2262 The numeric port number to which the server is currently bound.
1994 """ 2263 """
1995 return self._server.server_address[1] 2264 return self._server.server_address[1]
1996 2265
1997 2266
1998 if __name__ == '__main__': 2267 if __name__ == '__main__':
1999 pyauto_functional.Main() 2268 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