OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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() |
OLD | NEW |