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 | |
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 'gtt': { # GPU memory usage (graphics translation table) | |
1802 'min': ..., | |
1803 'max': ..., | |
1804 'end': ..., | |
1805 }, | |
1806 'mem_available': { ... }, # CPU available memory | |
1807 'mem_shared': { ... }, # CPU shared memory | |
1808 } | |
1809 """ | |
1810 logging.debug('Sampling memory information for %d seconds...' % duration) | |
1811 stats = { | |
1812 'gtt': [], | |
1813 'mem_available': [], | |
1814 'mem_shared': [], | |
1815 } | |
1816 | |
1817 for _ in xrange(duration): | |
1818 p = subprocess.Popen('grep bytes /sys/kernel/debug/dri/0/i915_gem_gtt', | |
Sonny
2012/04/26 04:55:18
GTT isn't guaranteed to always be there. It'll on
ihf
2012/04/26 05:19:17
Good catch on the checking for file existence.
dennis_jeffrey
2012/04/26 18:47:39
I modified the code to exclude GTT values if the f
| |
1819 stdout=subprocess.PIPE, | |
1820 shell=True) | |
1821 stdout = p.communicate()[0] | |
1822 | |
1823 # GPU memory. | |
1824 gtt_used = re.search( | |
1825 'Total [\d]+ objects, ([\d]+) bytes', stdout).group(1) | |
1826 stats['gtt'].append(int(gtt_used) / 1024.0) | |
1827 | |
1828 # CPU memory. | |
1829 stdout = '' | |
1830 with open('/proc/meminfo') as f: | |
1831 stdout = f.read() | |
1832 mem_free = re.search('MemFree:\s*([\d]+) kB', stdout).group(1) | |
1833 mem_dirty = re.search('Dirty:\s*([\d]+) kB', stdout).group(1) | |
Sonny
2012/04/26 04:55:18
Dirty Memory isn't really very interesting becaus
Sonny
2012/04/26 05:38:14
Now that I understand what mem_available is suppos
dennis_jeffrey
2012/04/26 18:47:39
See my comment at line 1846 below.
| |
1834 mem_active_file = re.search( | |
1835 'Active\(file\):\s*([\d]+) kB', stdout).group(1) | |
1836 mem_inactive_file = re.search( | |
1837 'Inactive\(file\):\s*([\d]+) kB', stdout).group(1) | |
1838 stats['mem_available'].append( | |
1839 (int(mem_active_file) + int(mem_inactive_file)) - int(mem_dirty) + | |
1840 int(mem_free)) | |
Sonny
2012/04/26 04:55:18
I'm not sure what mem_available is supposed to mea
ihf
2012/04/26 05:19:17
This was suggested by James as it is used for disc
James Cook
2012/04/26 05:25:59
It's apparently what Luigi uses in the kernel to p
Sonny
2012/04/26 05:38:14
Ah.. I see. That's not actually exactly true eith
dennis_jeffrey
2012/04/26 18:47:39
Let me know how the computation for available memo
Sonny
2012/04/26 22:25:40
so it should be
MemFree + Inactive file + active f
dennis_jeffrey
2012/04/28 00:21:35
Done.
| |
1841 | |
1842 mem_shared = re.search('Shmem:\s*([\d]+) kB', stdout).group(1) | |
1843 stats['mem_shared'].append(int(mem_shared)) | |
1844 | |
1845 time.sleep(1) | |
1846 | |
Sonny
2012/04/26 04:55:18
Also weren't we going to get the memory stats from
ihf
2012/04/26 05:19:17
First time I read about it.
Sonny
2012/04/26 05:38:14
we want the private and shared and possibly the ja
dennis_jeffrey
2012/04/26 18:47:39
Yes, there is lots more information that people wa
Sonny
2012/04/26 22:25:40
Ok... I just feel like we'll be adding more stuff
dennis_jeffrey
2012/04/28 00:21:35
We're now recording more different types of memory
| |
1847 # Compute min, max, and ending values to return. | |
1848 result = {} | |
1849 for measurement_type in stats: | |
1850 values = stats[measurement_type] | |
1851 result[measurement_type] = { | |
1852 'min': min(values), | |
1853 'max': max(values), | |
1854 'end': values[-1], | |
1855 } | |
1856 | |
1857 return result | |
1858 | |
1859 def _RecordMemoryStats(self, description, when, duration): | |
1860 """Outputs memory statistics to be graphed. | |
1861 | |
1862 Args: | |
1863 description: A string description for the test. Should not contain | |
1864 spaces. For example, 'MemCtrl'. | |
1865 when: A string description of when the memory stats are being recorded | |
1866 during test execution (since memory stats may be recorded multiple | |
1867 times during a test execution at certain "interesting" times). Should | |
1868 not contain spaces. | |
1869 duration: The number of seconds to sample data before outputting the | |
1870 memory statistics. | |
1871 """ | |
1872 mem = self._GetMemoryStats(duration) | |
1873 measurement_types = [ | |
1874 ('gtt', 'GTT'), | |
1875 ('mem_available', 'MemAvail'), | |
1876 ('mem_shared', 'MemShare'), | |
1877 ] | |
1878 for type_key, type_string in measurement_types: | |
1879 self._OutputPerfGraphValue( | |
1880 '%s-Min%s-%s' % (description, type_string, when), | |
1881 mem[type_key]['min'], 'KB', '%s-%s' % (description, type_string)) | |
1882 self._OutputPerfGraphValue( | |
1883 '%s-Max%s-%s' % (description, type_string, when), | |
1884 mem[type_key]['max'], 'KB', '%s-%s' % (description, type_string)) | |
1885 self._OutputPerfGraphValue( | |
1886 '%s-End%s-%s' % (description, type_string, when), | |
1887 mem[type_key]['end'], 'KB', '%s-%s' % (description, type_string)) | |
1888 | |
1889 def _RunTest(self, tabs, description, duration): | |
1890 """Runs a general memory test. | |
1891 | |
1892 Args: | |
1893 tabs: A list of strings representing the URLs of the websites to open | |
1894 during this test. | |
1895 description: A string description for the test. Should not contain | |
1896 spaces. For example, 'MemCtrl'. | |
1897 duration: The number of seconds to sample data before outputting memory | |
1898 statistics. | |
1899 """ | |
1900 self._RecordMemoryStats(description, '0Tabs0', duration) | |
1901 | |
1902 for iteration_num in xrange(2): | |
1903 for site in tabs: | |
1904 self.AppendTab(pyauto.GURL(site)) | |
1905 | |
1906 self._RecordMemoryStats(description, | |
1907 '%dTabs%d' % (len(tabs), iteration_num + 1), | |
1908 duration) | |
Sonny
2012/04/26 04:55:18
I'm not sure we want to record after opening every
ihf
2012/04/26 05:19:17
This works exactly as you want it.
Sonny
2012/04/26 05:38:14
Ahh oops, didn't see the indent, thanks.
dennis_jeffrey
2012/04/26 18:47:39
Done - working as intended.
| |
1909 | |
1910 for _ in xrange(len(tabs)): | |
1911 self.GetBrowserWindow(0).GetTab(1).Close(True) | |
1912 | |
1913 self._RecordMemoryStats(description, '0Tabs%d' % (iteration_num + 1), | |
1914 duration) | |
1915 | |
1916 def testOpenCloseTabsControl(self): | |
1917 """Measures memory usage when opening/closing tabs to about:blank.""" | |
1918 tabs = ['about:blank'] * 10 | |
1919 self._RunTest(tabs, 'MemCtrl', 15) | |
1920 | |
1921 | |
1768 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): | 1922 class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
1769 """Request handler for the local performance test server.""" | 1923 """Request handler for the local performance test server.""" |
1770 | 1924 |
1771 def _IgnoreHandler(self, unused_args): | 1925 def _IgnoreHandler(self, unused_args): |
1772 """A GET request handler that simply replies with status code 200. | 1926 """A GET request handler that simply replies with status code 200. |
1773 | 1927 |
1774 Args: | 1928 Args: |
1775 unused_args: A dictionary of arguments for the current GET request. | 1929 unused_args: A dictionary of arguments for the current GET request. |
1776 The arguments are ignored. | 1930 The arguments are ignored. |
1777 """ | 1931 """ |
(...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. | 2144 """Identifies the port number to which the server is currently bound. |
1991 | 2145 |
1992 Returns: | 2146 Returns: |
1993 The numeric port number to which the server is currently bound. | 2147 The numeric port number to which the server is currently bound. |
1994 """ | 2148 """ |
1995 return self._server.server_address[1] | 2149 return self._server.server_address[1] |
1996 | 2150 |
1997 | 2151 |
1998 if __name__ == '__main__': | 2152 if __name__ == '__main__': |
1999 pyauto_functional.Main() | 2153 pyauto_functional.Main() |
OLD | NEW |