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 """Performance tests for Chrome Endure (long-running perf tests on Chrome). | 6 """Performance tests for Chrome Endure (long-running perf tests on Chrome). |
7 | 7 |
8 This module accepts the following environment variable inputs: | 8 This module accepts the following environment variable inputs: |
9 TEST_LENGTH: The number of seconds in which to run each test. | 9 TEST_LENGTH: The number of seconds in which to run each test. |
10 PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling | 10 PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling |
(...skipping 27 matching lines...) Expand all Loading... |
38 import selenium.common.exceptions | 38 import selenium.common.exceptions |
39 from selenium.webdriver.support.ui import WebDriverWait | 39 from selenium.webdriver.support.ui import WebDriverWait |
40 import webpagereplay | 40 import webpagereplay |
41 | 41 |
42 | 42 |
43 class NotSupportedEnvironmentError(RuntimeError): | 43 class NotSupportedEnvironmentError(RuntimeError): |
44 """Represent an error raised since the environment (OS) is not supported.""" | 44 """Represent an error raised since the environment (OS) is not supported.""" |
45 pass | 45 pass |
46 | 46 |
47 | 47 |
| 48 class DeepMemoryProfiler(object): |
| 49 """Controls Deep Memory Profiler (dmprof) for endurance tests.""" |
| 50 DEEP_MEMORY_PROFILE = False |
| 51 DEEP_MEMORY_PROFILE_SAVE = False |
| 52 |
| 53 _DMPROF_DIR_PATH = os.path.join( |
| 54 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, |
| 55 'tools', 'deep_memory_profiler') |
| 56 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') |
| 57 |
| 58 def __init__(self): |
| 59 self._enabled = self.GetEnvironmentVariable( |
| 60 'DEEP_MEMORY_PROFILE', bool, self.DEEP_MEMORY_PROFILE) |
| 61 self._save = self.GetEnvironmentVariable( |
| 62 'DEEP_MEMORY_PROFILE_SAVE', bool, self.DEEP_MEMORY_PROFILE_SAVE) |
| 63 self._json_file = None |
| 64 self._last_json_filename = '' |
| 65 self._proc = None |
| 66 self._last_time = -1.0 |
| 67 |
| 68 def __nonzero__(self): |
| 69 return self._enabled |
| 70 |
| 71 @staticmethod |
| 72 def GetEnvironmentVariable(env_name, converter, default): |
| 73 """Returns a converted environment variable for Deep Memory Profiler. |
| 74 |
| 75 Args: |
| 76 env_name: A string name of an environment variable. |
| 77 converter: A function taking a string to convert an environment variable. |
| 78 default: A value used if the environment variable is not specified. |
| 79 |
| 80 Returns: |
| 81 A value converted from the environment variable with 'converter'. |
| 82 """ |
| 83 return converter(os.environ.get(env_name, default)) |
| 84 |
| 85 def SetUp(self, is_linux): |
| 86 """Sets up Deep Memory Profiler settings for a Chrome process. |
| 87 |
| 88 It sets environment variables and makes a working directory. |
| 89 """ |
| 90 if not self._enabled: |
| 91 return |
| 92 |
| 93 if not is_linux: |
| 94 raise NotSupportedEnvironmentError( |
| 95 'Deep Memory Profiler is not supported in this environment (OS).') |
| 96 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') |
| 97 self._tempdir = tempfile.mkdtemp(prefix=dir_prefix) |
| 98 os.environ['HEAPPROFILE'] = os.path.join(self._tempdir, 'endure') |
| 99 os.environ['HEAP_PROFILE_MMAP'] = '1' |
| 100 os.environ['DEEP_HEAP_PROFILE'] = '1' |
| 101 |
| 102 def TearDown(self): |
| 103 """Tear down Deep Memory Profiler settings for the Chrome process. |
| 104 |
| 105 It removes the environment variables and the temporary directory. |
| 106 Call it after Chrome finishes. Chrome may dump last files at the end. |
| 107 """ |
| 108 if not self._enabled: |
| 109 return |
| 110 |
| 111 del os.environ['DEEP_HEAP_PROFILE'] |
| 112 del os.environ['HEAP_PROFILE_MMAP'] |
| 113 del os.environ['HEAPPROFILE'] |
| 114 if not self._save and self._tempdir: |
| 115 pyauto_utils.RemovePath(self._tempdir) |
| 116 |
| 117 def LogFirstMessage(self): |
| 118 """Logs first messages.""" |
| 119 if not self._enabled: |
| 120 return |
| 121 |
| 122 logging.info('Running with the Deep Memory Profiler.') |
| 123 if self._save: |
| 124 logging.info(' Dumped files won\'t be cleaned.') |
| 125 else: |
| 126 logging.info(' Dumped files will be cleaned up after every test.') |
| 127 |
| 128 def StartProfiler(self, proc_info, is_last): |
| 129 """Starts Deep Memory Profiler in background.""" |
| 130 if not self._enabled: |
| 131 return |
| 132 |
| 133 logging.info(' Profiling with the Deep Memory Profiler...') |
| 134 |
| 135 # Wait for a running dmprof process for last _GetPerformanceStat call to |
| 136 # cover last dump files. |
| 137 if is_last: |
| 138 logging.info(' Waiting for the last dmprof.') |
| 139 self._WaitForDeepMemoryProfiler() |
| 140 |
| 141 if self._proc and self._proc.poll() is None: |
| 142 logging.info(' Last dmprof is still running.') |
| 143 else: |
| 144 if self._json_file: |
| 145 self._last_json_filename = self._json_file.name |
| 146 self._json_file.close() |
| 147 self._json_file = None |
| 148 first_dump = '' |
| 149 last_dump = '' |
| 150 for filename in sorted(os.listdir(self._tempdir)): |
| 151 if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'], |
| 152 filename): |
| 153 logging.info(' Profiled dump file: %s' % filename) |
| 154 last_dump = filename |
| 155 if not first_dump: |
| 156 first_dump = filename |
| 157 if first_dump: |
| 158 logging.info(' First dump file: %s' % first_dump) |
| 159 matched = re.match('^endure.\d+.(\d+).heap$', last_dump) |
| 160 last_sequence_id = matched.group(1) |
| 161 self._json_file = open( |
| 162 os.path.join(self._tempdir, |
| 163 'endure.%05d.%s.json' % (proc_info['tab_pid'], |
| 164 last_sequence_id)), 'w+') |
| 165 self._proc = subprocess.Popen( |
| 166 '%s json %s' % (self._DMPROF_SCRIPT_PATH, |
| 167 os.path.join(self._tempdir, first_dump)), |
| 168 shell=True, stdout=self._json_file) |
| 169 # Wait only when it is the last profiling. dmprof may take long time. |
| 170 if is_last: |
| 171 self._WaitForDeepMemoryProfiler() |
| 172 else: |
| 173 logging.info(' No dump files.') |
| 174 |
| 175 def ParseResultAndOutputPerfGraphValues( |
| 176 self, webapp_name, test_description, output_perf_graph_value): |
| 177 """Parses Deep Memory Profiler result, and outputs perf graph values.""" |
| 178 if not self._enabled: |
| 179 return |
| 180 |
| 181 results = {} |
| 182 if self._last_json_filename: |
| 183 json_data = {} |
| 184 with open(self._last_json_filename) as json_f: |
| 185 json_data = json.load(json_f) |
| 186 if json_data['version'] == 'JSON_DEEP_1': |
| 187 results = json_data['snapshots'] |
| 188 elif json_data['version'] == 'JSON_DEEP_2': |
| 189 results = json_data['policies']['l2']['snapshots'] |
| 190 if results and results[-1]['second'] > self._last_time: |
| 191 started = False |
| 192 for legend in json_data['policies']['l2']['legends']: |
| 193 if legend == 'FROM_HERE_FOR_TOTAL': |
| 194 started = True |
| 195 elif legend == 'UNTIL_HERE_FOR_TOTAL': |
| 196 break |
| 197 elif started: |
| 198 output_perf_graph_value( |
| 199 legend.encode('utf-8'), [ |
| 200 (int(round(snapshot['second'])), snapshot[legend] / 1024) |
| 201 for snapshot in results |
| 202 if snapshot['second'] > self._last_time], |
| 203 'KB', |
| 204 graph_name='%s%s-DMP' % (webapp_name, test_description), |
| 205 units_x='seconds', is_stacked=True) |
| 206 self._last_time = results[-1]['second'] |
| 207 |
| 208 def _WaitForDeepMemoryProfiler(self): |
| 209 """Waits for the Deep Memory Profiler to finish if running.""" |
| 210 if not self._enabled or not self._proc: |
| 211 return |
| 212 |
| 213 self._proc.wait() |
| 214 self._proc = None |
| 215 if self._json_file: |
| 216 self._last_json_filename = self._json_file.name |
| 217 self._json_file.close() |
| 218 self._json_file = None |
| 219 |
| 220 |
48 class ChromeEndureBaseTest(perf.BasePerfTest): | 221 class ChromeEndureBaseTest(perf.BasePerfTest): |
49 """Implements common functionality for all Chrome Endure tests. | 222 """Implements common functionality for all Chrome Endure tests. |
50 | 223 |
51 All Chrome Endure test classes should inherit from this class. | 224 All Chrome Endure test classes should inherit from this class. |
52 """ | 225 """ |
53 | 226 |
54 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. | 227 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. |
55 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. | 228 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
56 # TODO(dennisjeffrey): Do we still need to tolerate errors? | 229 # TODO(dennisjeffrey): Do we still need to tolerate errors? |
57 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. | 230 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. |
58 _DEEP_MEMORY_PROFILE = False | |
59 _DEEP_MEMORY_PROFILE_SAVE = False | |
60 | |
61 _DMPROF_DIR_PATH = os.path.join( | |
62 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, | |
63 'tools', 'deep_memory_profiler') | |
64 | |
65 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') | |
66 | 231 |
67 def setUp(self): | 232 def setUp(self): |
68 # The Web Page Replay environment variables must be parsed before | 233 # The Web Page Replay environment variables must be parsed before |
69 # perf.BasePerfTest.setUp() | 234 # perf.BasePerfTest.setUp() |
70 self._ParseReplayEnv() | 235 self._ParseReplayEnv() |
71 # The environment variables for the Deep Memory Profiler must be set | 236 # The environment variables for the Deep Memory Profiler must be set |
72 # before perf.BasePerfTest.setUp() to inherit them to Chrome. | 237 # before perf.BasePerfTest.setUp() to inherit them to Chrome. |
73 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( | 238 self._dmprof = DeepMemoryProfiler() |
74 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 239 if self._dmprof: |
75 | 240 self._dmprof.SetUp(self.IsLinux()) |
76 if self._deep_memory_profile: | |
77 if not self.IsLinux(): | |
78 raise NotSupportedEnvironmentError( | |
79 'Deep Memory Profiler is not supported in this environment (OS).') | |
80 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') | |
81 self._deep_tempdir = tempfile.mkdtemp(prefix=dir_prefix) | |
82 os.environ['HEAPPROFILE'] = os.path.join(self._deep_tempdir, 'endure') | |
83 os.environ['HEAP_PROFILE_MMAP'] = 'True' | |
84 os.environ['DEEP_HEAP_PROFILE'] = 'True' | |
85 | 241 |
86 perf.BasePerfTest.setUp(self) | 242 perf.BasePerfTest.setUp(self) |
87 | 243 |
88 self._test_length_sec = int( | 244 self._test_length_sec = int( |
89 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) | 245 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) |
90 self._get_perf_stats_interval = int( | 246 self._get_perf_stats_interval = int( |
91 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) | 247 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) |
92 | 248 |
93 self._deep_memory_profile_save = self._GetDeepMemoryProfileEnv( | |
94 'DEEP_MEMORY_PROFILE_SAVE', bool, self._DEEP_MEMORY_PROFILE_SAVE) | |
95 | |
96 logging.info('Running test for %d seconds.', self._test_length_sec) | 249 logging.info('Running test for %d seconds.', self._test_length_sec) |
97 logging.info('Gathering perf stats every %d seconds.', | 250 logging.info('Gathering perf stats every %d seconds.', |
98 self._get_perf_stats_interval) | 251 self._get_perf_stats_interval) |
99 if self._deep_memory_profile: | 252 |
100 logging.info('Running with the Deep Memory Profiler.') | 253 if self._dmprof: |
101 if self._deep_memory_profile_save: | 254 self._dmprof.LogFirstMessage() |
102 logging.info(' Dumped files won\'t be cleaned.') | |
103 else: | |
104 logging.info(' Dumped files will be cleaned up after every test.') | |
105 | 255 |
106 # Set up a remote inspector client associated with tab 0. | 256 # Set up a remote inspector client associated with tab 0. |
107 logging.info('Setting up connection to remote inspector...') | 257 logging.info('Setting up connection to remote inspector...') |
108 self._remote_inspector_client = ( | 258 self._remote_inspector_client = ( |
109 remote_inspector_client.RemoteInspectorClient()) | 259 remote_inspector_client.RemoteInspectorClient()) |
110 logging.info('Connection to remote inspector set up successfully.') | 260 logging.info('Connection to remote inspector set up successfully.') |
111 | 261 |
112 self._test_start_time = 0 | 262 self._test_start_time = 0 |
113 self._num_errors = 0 | 263 self._num_errors = 0 |
114 self._events_to_output = [] | 264 self._events_to_output = [] |
115 self._deep_memory_profile_json_file = None | |
116 self._deep_memory_profile_last_json_filename = '' | |
117 self._deep_memory_profile_proc = None | |
118 self._StartReplayServerIfNecessary() | 265 self._StartReplayServerIfNecessary() |
119 | 266 |
120 def tearDown(self): | 267 def tearDown(self): |
121 logging.info('Terminating connection to remote inspector...') | 268 logging.info('Terminating connection to remote inspector...') |
122 self._remote_inspector_client.Stop() | 269 self._remote_inspector_client.Stop() |
123 logging.info('Connection to remote inspector terminated.') | 270 logging.info('Connection to remote inspector terminated.') |
124 if self._deep_memory_profile: | |
125 del os.environ['DEEP_HEAP_PROFILE'] | |
126 del os.environ['HEAP_PROFILE_MMAP'] | |
127 del os.environ['HEAPPROFILE'] | |
128 | 271 |
129 # Must be done at end of this function except for post-cleaning after | 272 # Must be done at end of this function except for post-cleaning after |
130 # Chrome finishes. | 273 # Chrome finishes. |
131 perf.BasePerfTest.tearDown(self) | 274 perf.BasePerfTest.tearDown(self) |
132 | 275 |
133 # Remove the temporary directory after Chrome finishes in tearDown. | |
134 if (self._deep_memory_profile and | |
135 not self._deep_memory_profile_save and | |
136 self._deep_tempdir): | |
137 pyauto_utils.RemovePath(self._deep_tempdir) | |
138 # Must be done after perf.BasePerfTest.tearDown() | 276 # Must be done after perf.BasePerfTest.tearDown() |
139 self._StopReplayServerIfNecessary() | 277 self._StopReplayServerIfNecessary() |
| 278 if self._dmprof: |
| 279 self._dmprof.TearDown() |
140 | 280 |
141 def _GetArchiveName(self): | 281 def _GetArchiveName(self): |
142 """Return the Web Page Replay archive name that corresponds to a test. | 282 """Return the Web Page Replay archive name that corresponds to a test. |
143 | 283 |
144 Override this function to return the name of an archive that | 284 Override this function to return the name of an archive that |
145 corresponds to the test, e.g "ChromeEndureGmailTest.wpr". | 285 corresponds to the test, e.g "ChromeEndureGmailTest.wpr". |
146 | 286 |
147 Returns: | 287 Returns: |
148 None, by default no archive name is provided. | 288 None, by default no archive name is provided. |
149 """ | 289 """ |
(...skipping 23 matching lines...) Expand all Loading... |
173 '_GetArchiveName() in your test?') | 313 '_GetArchiveName() in your test?') |
174 else: | 314 else: |
175 if self._archive_path and os.path.exists(self._archive_path): | 315 if self._archive_path and os.path.exists(self._archive_path): |
176 self._use_wpr = True | 316 self._use_wpr = True |
177 else: | 317 else: |
178 self._use_wpr = False | 318 self._use_wpr = False |
179 logging.info( | 319 logging.info( |
180 'Skipping Web Page Replay since archive file %sdoes not exist.', | 320 'Skipping Web Page Replay since archive file %sdoes not exist.', |
181 self._archive_path + ' ' if self._archive_path else '') | 321 self._archive_path + ' ' if self._archive_path else '') |
182 | 322 |
183 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): | |
184 """Returns a converted environment variable for the Deep Memory Profiler. | |
185 | |
186 Args: | |
187 env_name: A string name of an environment variable. | |
188 converter: A function taking a string to convert an environment variable. | |
189 default: A value used if the environment variable is not specified. | |
190 | |
191 Returns: | |
192 A value converted from the environment variable with 'converter'. | |
193 """ | |
194 return converter(os.environ.get(env_name, default)) | |
195 | |
196 def _WaitForDeepMemoryProfiler(self): | |
197 """Waits for the Deep Memory Profiler to finish if running.""" | |
198 if self._deep_memory_profile and self._deep_memory_profile_proc: | |
199 self._deep_memory_profile_proc.wait() | |
200 self._deep_memory_profile_proc = None | |
201 if self._deep_memory_profile_json_file: | |
202 self._deep_memory_profile_last_json_filename = ( | |
203 self._deep_memory_profile_json_file.name) | |
204 self._deep_memory_profile_json_file.close() | |
205 self._deep_memory_profile_json_file = None | |
206 | |
207 def ExtraChromeFlags(self): | 323 def ExtraChromeFlags(self): |
208 """Ensures Chrome is launched with custom flags. | 324 """Ensures Chrome is launched with custom flags. |
209 | 325 |
210 Returns: | 326 Returns: |
211 A list of extra flags to pass to Chrome when it is launched. | 327 A list of extra flags to pass to Chrome when it is launched. |
212 """ | 328 """ |
213 # The same with setUp, but need to fetch the environment variable since | 329 # The same with setUp, but need to fetch the environment variable since |
214 # ExtraChromeFlags is called before setUp. | 330 # ExtraChromeFlags is called before setUp. |
215 deep_memory_profile = self._GetDeepMemoryProfileEnv( | 331 deep_memory_profile = DeepMemoryProfiler.GetEnvironmentVariable( |
216 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 332 'DEEP_MEMORY_PROFILE', bool, DeepMemoryProfiler.DEEP_MEMORY_PROFILE) |
217 | 333 |
218 # Ensure Chrome enables remote debugging on port 9222. This is required to | 334 # Ensure Chrome enables remote debugging on port 9222. This is required to |
219 # interact with Chrome's remote inspector. | 335 # interact with Chrome's remote inspector. |
220 extra_flags = ['--remote-debugging-port=9222'] | 336 extra_flags = ['--remote-debugging-port=9222'] |
221 if deep_memory_profile: | 337 if deep_memory_profile: |
222 extra_flags.append('--no-sandbox') | 338 extra_flags.append('--no-sandbox') |
223 if self._use_wpr: | 339 if self._use_wpr: |
224 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) | 340 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) |
225 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags | 341 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags |
226 | 342 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 do_scenario: A callable to be invoked that implements the scenario to be | 377 do_scenario: A callable to be invoked that implements the scenario to be |
262 performed by this test. The callable is invoked iteratively for the | 378 performed by this test. The callable is invoked iteratively for the |
263 duration of the test. | 379 duration of the test. |
264 frame_xpath: The string xpath of the frame in which to inject javascript | 380 frame_xpath: The string xpath of the frame in which to inject javascript |
265 to clear chromedriver's cache (a temporary workaround until the | 381 to clear chromedriver's cache (a temporary workaround until the |
266 WebDriver team changes how they handle their DOM node cache). | 382 WebDriver team changes how they handle their DOM node cache). |
267 """ | 383 """ |
268 self._num_errors = 0 | 384 self._num_errors = 0 |
269 self._test_start_time = time.time() | 385 self._test_start_time = time.time() |
270 last_perf_stats_time = time.time() | 386 last_perf_stats_time = time.time() |
271 if self._deep_memory_profile: | 387 if self._dmprof: |
272 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') | 388 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') |
273 self._GetPerformanceStats( | 389 self._GetPerformanceStats( |
274 webapp_name, test_description, tab_title_substring) | 390 webapp_name, test_description, tab_title_substring) |
275 self._iteration_num = 0 # Available to |do_scenario| if needed. | 391 self._iteration_num = 0 # Available to |do_scenario| if needed. |
276 | 392 |
277 self._remote_inspector_client.StartTimelineEventMonitoring( | 393 self._remote_inspector_client.StartTimelineEventMonitoring( |
278 self._OnTimelineEvent) | 394 self._OnTimelineEvent) |
279 | 395 |
280 while time.time() - self._test_start_time < self._test_length_sec: | 396 while time.time() - self._test_start_time < self._test_length_sec: |
281 self._iteration_num += 1 | 397 self._iteration_num += 1 |
282 | 398 |
283 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: | 399 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: |
284 logging.error('Error count threshold (%d) reached. Terminating test ' | 400 logging.error('Error count threshold (%d) reached. Terminating test ' |
285 'early.' % self._ERROR_COUNT_THRESHOLD) | 401 'early.' % self._ERROR_COUNT_THRESHOLD) |
286 break | 402 break |
287 | 403 |
288 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: | 404 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: |
289 last_perf_stats_time = time.time() | 405 last_perf_stats_time = time.time() |
290 if self._deep_memory_profile: | 406 if self._dmprof: |
291 self.HeapProfilerDump('renderer', 'Chrome Endure') | 407 self.HeapProfilerDump('renderer', 'Chrome Endure') |
292 self._GetPerformanceStats( | 408 self._GetPerformanceStats( |
293 webapp_name, test_description, tab_title_substring) | 409 webapp_name, test_description, tab_title_substring) |
294 | 410 |
295 if self._iteration_num % 10 == 0: | 411 if self._iteration_num % 10 == 0: |
296 remaining_time = self._test_length_sec - (time.time() - | 412 remaining_time = self._test_length_sec - (time.time() - |
297 self._test_start_time) | 413 self._test_start_time) |
298 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % | 414 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % |
299 (self._iteration_num, remaining_time)) | 415 (self._iteration_num, remaining_time)) |
300 | 416 |
301 do_scenario() | 417 do_scenario() |
302 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the | 418 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the |
303 # results of Chrome Endure. | 419 # results of Chrome Endure. |
304 # TODO(dennisjeffrey): Once the WebDriver team implements changes to | 420 # TODO(dennisjeffrey): Once the WebDriver team implements changes to |
305 # handle their DOM node cache differently, we need to revisit this. It | 421 # handle their DOM node cache differently, we need to revisit this. It |
306 # may no longer be necessary at that point to forcefully delete the cache. | 422 # may no longer be necessary at that point to forcefully delete the cache. |
307 # Additionally, the Javascript below relies on an internal property of | 423 # Additionally, the Javascript below relies on an internal property of |
308 # WebDriver that may change at any time. This is only a temporary | 424 # WebDriver that may change at any time. This is only a temporary |
309 # workaround to stabilize the Chrome Endure test results. | 425 # workaround to stabilize the Chrome Endure test results. |
310 js = """ | 426 js = """ |
311 (function() { | 427 (function() { |
312 delete document.$wdc_; | 428 delete document.$wdc_; |
313 window.domAutomationController.send('done'); | 429 window.domAutomationController.send('done'); |
314 })(); | 430 })(); |
315 """ | 431 """ |
316 self.ExecuteJavascript(js, frame_xpath=frame_xpath) | 432 self.ExecuteJavascript(js, frame_xpath=frame_xpath) |
317 | 433 |
318 self._remote_inspector_client.StopTimelineEventMonitoring() | 434 self._remote_inspector_client.StopTimelineEventMonitoring() |
319 | 435 |
320 if self._deep_memory_profile: | 436 if self._dmprof: |
321 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') | 437 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') |
322 self._GetPerformanceStats( | 438 self._GetPerformanceStats( |
323 webapp_name, test_description, tab_title_substring, is_last=True) | 439 webapp_name, test_description, tab_title_substring, is_last=True) |
324 | 440 |
325 def _GetProcessInfo(self, tab_title_substring): | 441 def _GetProcessInfo(self, tab_title_substring): |
326 """Gets process info associated with an open browser/tab. | 442 """Gets process info associated with an open browser/tab. |
327 | 443 |
328 Args: | 444 Args: |
329 tab_title_substring: A unique substring contained within the title of | 445 tab_title_substring: A unique substring contained within the title of |
330 the tab to use; needed for locating the tab info. | 446 the tab to use; needed for locating the tab info. |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 the tab to use, for identifying the appropriate tab. | 507 the tab to use, for identifying the appropriate tab. |
392 is_last: A boolean value which should be True if it's the last call of | 508 is_last: A boolean value which should be True if it's the last call of |
393 _GetPerformanceStats. The default is False. | 509 _GetPerformanceStats. The default is False. |
394 """ | 510 """ |
395 logging.info('Gathering performance stats...') | 511 logging.info('Gathering performance stats...') |
396 elapsed_time = int(round(time.time() - self._test_start_time)) | 512 elapsed_time = int(round(time.time() - self._test_start_time)) |
397 | 513 |
398 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() | 514 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() |
399 proc_info = self._GetProcessInfo(tab_title_substring) | 515 proc_info = self._GetProcessInfo(tab_title_substring) |
400 | 516 |
401 # Run Deep Memory Profiler in background. | 517 if self._dmprof: |
402 if self._deep_memory_profile: | 518 self._dmprof.StartProfiler(proc_info, is_last) |
403 logging.info(' Profiling with the Deep Memory Profiler...') | |
404 | |
405 # Wait for a running dmprof process for last _GetPerformanceStat call to | |
406 # cover last dump files. | |
407 if is_last: | |
408 logging.info(' Waiting for the last dmprof.') | |
409 self._WaitForDeepMemoryProfiler() | |
410 | |
411 if (self._deep_memory_profile_proc and | |
412 self._deep_memory_profile_proc.poll() is None): | |
413 logging.info(' Last dmprof is still running.') | |
414 else: | |
415 if self._deep_memory_profile_json_file: | |
416 self._deep_memory_profile_last_json_filename = ( | |
417 self._deep_memory_profile_json_file.name) | |
418 self._deep_memory_profile_json_file.close() | |
419 self._deep_memory_profile_json_file = None | |
420 first_dump = '' | |
421 last_dump = '' | |
422 for filename in sorted(os.listdir(self._deep_tempdir)): | |
423 if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'], | |
424 filename): | |
425 logging.info(' Profiled dump file: %s' % filename) | |
426 last_dump = filename | |
427 if not first_dump: | |
428 first_dump = filename | |
429 if first_dump: | |
430 logging.info(' First dump file: %s' % first_dump) | |
431 matched= re.match('^endure.\d+.(\d+).heap$', last_dump) | |
432 last_sequence_id = matched.group(1) | |
433 self._deep_memory_profile_json_file = open( | |
434 os.path.join(self._deep_tempdir, | |
435 'endure.%05d.%s.json' % (proc_info['tab_pid'], | |
436 last_sequence_id)), 'w+') | |
437 self._deep_memory_profile_proc = subprocess.Popen( | |
438 '%s json %s' % (self._DMPROF_SCRIPT_PATH, | |
439 os.path.join(self._deep_tempdir, first_dump)), | |
440 shell=True, stdout=self._deep_memory_profile_json_file) | |
441 # Don't wait for the new process since dmprof may take long time. | |
442 | |
443 if is_last: | |
444 self._WaitForDeepMemoryProfiler() | |
445 | |
446 else: | |
447 logging.info(' No dump files.') | |
448 | 519 |
449 # DOM node count. | 520 # DOM node count. |
450 dom_node_count = memory_counts['DOMNodeCount'] | 521 dom_node_count = memory_counts['DOMNodeCount'] |
451 self._OutputPerfGraphValue( | 522 self._OutputPerfGraphValue( |
452 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', | 523 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', |
453 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), | 524 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), |
454 units_x='seconds') | 525 units_x='seconds') |
455 | 526 |
456 # Event listener count. | 527 # Event listener count. |
457 event_listener_count = memory_counts['EventListenerCount'] | 528 event_listener_count = memory_counts['EventListenerCount'] |
(...skipping 25 matching lines...) Expand all Loading... |
483 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), | 554 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), |
484 units_x='seconds') | 555 units_x='seconds') |
485 | 556 |
486 # V8 memory allocated. | 557 # V8 memory allocated. |
487 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. | 558 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. |
488 self._OutputPerfGraphValue( | 559 self._OutputPerfGraphValue( |
489 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', | 560 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', |
490 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), | 561 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), |
491 units_x='seconds') | 562 units_x='seconds') |
492 | 563 |
493 # Deep Memory Profiler result. | 564 if self._dmprof: |
494 if self._deep_memory_profile: | 565 self._dmprof.ParseResultAndOutputPerfGraphValues( |
495 deep_memory_profile_results = {} | 566 webapp_name, test_description, self._OutputPerfGraphValue) |
496 if self._deep_memory_profile_last_json_filename: | |
497 json_data = {} | |
498 with open(self._deep_memory_profile_last_json_filename) as json_f: | |
499 json_data = json.load(json_f) | |
500 if json_data['version'] == 'JSON_DEEP_1': | |
501 deep_memory_profile_results = json_data['snapshots'] | |
502 elif json_data['version'] == 'JSON_DEEP_2': | |
503 deep_memory_profile_results = json_data['policies']['l0']['snapshots'] | |
504 if deep_memory_profile_results: | |
505 self._OutputPerfGraphValue( | |
506 'DMP-TCMallocUsed', [ | |
507 (snapshot['second'], snapshot['tc-used-all'] / 1024.0) | |
508 for snapshot in deep_memory_profile_results], | |
509 'KB', | |
510 graph_name='%s%s-DMP-TCUsed' % (webapp_name, test_description), | |
511 units_x='seconds') | |
512 # TODO(dmikurube): Output graph values (for multi-lined graphs), here. | |
513 # 'deep_memory_profile_results' is the value to be output. | |
514 | 567 |
515 logging.info(' Total DOM node count: %d nodes' % dom_node_count) | 568 logging.info(' Total DOM node count: %d nodes' % dom_node_count) |
516 logging.info(' Event listener count: %d listeners' % event_listener_count) | 569 logging.info(' Event listener count: %d listeners' % event_listener_count) |
517 logging.info(' Browser process private memory: %d KB' % | 570 logging.info(' Browser process private memory: %d KB' % |
518 proc_info['browser_private_mem']) | 571 proc_info['browser_private_mem']) |
519 logging.info(' Tab process private memory: %d KB' % | 572 logging.info(' Tab process private memory: %d KB' % |
520 proc_info['tab_private_mem']) | 573 proc_info['tab_private_mem']) |
521 logging.info(' V8 memory used: %f KB' % v8_mem_used) | 574 logging.info(' V8 memory used: %f KB' % v8_mem_used) |
522 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) | 575 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) |
523 | 576 |
(...skipping 718 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1242 if not os.path.exists(scripts): | 1295 if not os.path.exists(scripts): |
1243 raise IOError('Injected scripts %s not found.' % scripts) | 1296 raise IOError('Injected scripts %s not found.' % scripts) |
1244 replay_options = ['--inject_scripts', scripts] | 1297 replay_options = ['--inject_scripts', scripts] |
1245 if 'WPR_RECORD' in os.environ: | 1298 if 'WPR_RECORD' in os.environ: |
1246 replay_options.append('--append') | 1299 replay_options.append('--append') |
1247 return webpagereplay.ReplayServer(archive_path, replay_options) | 1300 return webpagereplay.ReplayServer(archive_path, replay_options) |
1248 | 1301 |
1249 | 1302 |
1250 if __name__ == '__main__': | 1303 if __name__ == '__main__': |
1251 pyauto_functional.Main() | 1304 pyauto_functional.Main() |
OLD | NEW |