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 | |
dennis_jeffrey
2012/09/11 20:57:29
maybe remove this blank line
Dai Mikurube (NOT FULLTIME)
2012/09/12 02:23:24
Done.
| |
173 else: | |
174 logging.info(' No dump files.') | |
175 | |
176 def ParseResultAndOutputPerfGraphValues( | |
177 self, webapp_name, test_description, output_perf_graph_value): | |
178 """Parses Deep Memory Profiler result, and outputs perf graph values.""" | |
179 if not self._enabled: | |
180 return | |
181 | |
182 results = {} | |
183 if self._last_json_filename: | |
184 json_data = {} | |
185 with open(self._last_json_filename) as json_f: | |
186 json_data = json.load(json_f) | |
187 if json_data['version'] == 'JSON_DEEP_1': | |
188 results = json_data['snapshots'] | |
189 elif json_data['version'] == 'JSON_DEEP_2': | |
190 results = json_data['policies']['l2']['snapshots'] | |
191 if results and results[-1]['second'] > self._last_time: | |
192 started = False | |
193 for legend in json_data['policies']['l2']['legends']: | |
194 if legend == 'FROM_HERE_FOR_TOTAL': | |
195 started = True | |
196 elif legend == 'UNTIL_HERE_FOR_TOTAL': | |
197 break | |
198 elif started: | |
199 output_perf_graph_value( | |
200 legend.encode('utf-8'), [ | |
201 (int(round(snapshot['second'])), snapshot[legend] / 1024) | |
202 for snapshot in results | |
203 if snapshot['second'] > self._last_time], | |
204 'KB', | |
205 graph_name='%s%s-DMP' % (webapp_name, test_description), | |
206 units_x='seconds', is_stacked=True) | |
207 self._last_time = results[-1]['second'] | |
208 | |
209 def _WaitForDeepMemoryProfiler(self): | |
210 """Waits for the Deep Memory Profiler to finish if running.""" | |
211 if not self._enabled or not self._proc: | |
212 return | |
213 | |
214 self._proc.wait() | |
215 self._proc = None | |
216 if self._json_file: | |
217 self._last_json_filename = self._json_file.name | |
218 self._json_file.close() | |
219 self._json_file = None | |
220 | |
221 | |
48 class ChromeEndureBaseTest(perf.BasePerfTest): | 222 class ChromeEndureBaseTest(perf.BasePerfTest): |
49 """Implements common functionality for all Chrome Endure tests. | 223 """Implements common functionality for all Chrome Endure tests. |
50 | 224 |
51 All Chrome Endure test classes should inherit from this class. | 225 All Chrome Endure test classes should inherit from this class. |
52 """ | 226 """ |
53 | 227 |
54 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. | 228 _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. | 229 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
56 # TODO(dennisjeffrey): Do we still need to tolerate errors? | 230 # TODO(dennisjeffrey): Do we still need to tolerate errors? |
57 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. | 231 _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 | 232 |
67 def setUp(self): | 233 def setUp(self): |
68 # The Web Page Replay environment variables must be parsed before | 234 # The Web Page Replay environment variables must be parsed before |
69 # perf.BasePerfTest.setUp() | 235 # perf.BasePerfTest.setUp() |
70 self._ParseReplayEnv() | 236 self._ParseReplayEnv() |
71 # The environment variables for the Deep Memory Profiler must be set | 237 # The environment variables for the Deep Memory Profiler must be set |
72 # before perf.BasePerfTest.setUp() to inherit them to Chrome. | 238 # before perf.BasePerfTest.setUp() to inherit them to Chrome. |
73 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( | 239 self._dmprof = DeepMemoryProfiler() |
74 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 240 if self._dmprof: |
75 | 241 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 | 242 |
86 perf.BasePerfTest.setUp(self) | 243 perf.BasePerfTest.setUp(self) |
87 | 244 |
88 self._test_length_sec = int( | 245 self._test_length_sec = int( |
89 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) | 246 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) |
90 self._get_perf_stats_interval = int( | 247 self._get_perf_stats_interval = int( |
91 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) | 248 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) |
92 | 249 |
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) | 250 logging.info('Running test for %d seconds.', self._test_length_sec) |
97 logging.info('Gathering perf stats every %d seconds.', | 251 logging.info('Gathering perf stats every %d seconds.', |
98 self._get_perf_stats_interval) | 252 self._get_perf_stats_interval) |
99 if self._deep_memory_profile: | 253 |
100 logging.info('Running with the Deep Memory Profiler.') | 254 if self._dmprof: |
101 if self._deep_memory_profile_save: | 255 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 | 256 |
106 # Set up a remote inspector client associated with tab 0. | 257 # Set up a remote inspector client associated with tab 0. |
107 logging.info('Setting up connection to remote inspector...') | 258 logging.info('Setting up connection to remote inspector...') |
108 self._remote_inspector_client = ( | 259 self._remote_inspector_client = ( |
109 remote_inspector_client.RemoteInspectorClient()) | 260 remote_inspector_client.RemoteInspectorClient()) |
110 logging.info('Connection to remote inspector set up successfully.') | 261 logging.info('Connection to remote inspector set up successfully.') |
111 | 262 |
112 self._test_start_time = 0 | 263 self._test_start_time = 0 |
113 self._num_errors = 0 | 264 self._num_errors = 0 |
114 self._events_to_output = [] | 265 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() | 266 self._StartReplayServerIfNecessary() |
119 | 267 |
120 def tearDown(self): | 268 def tearDown(self): |
121 logging.info('Terminating connection to remote inspector...') | 269 logging.info('Terminating connection to remote inspector...') |
122 self._remote_inspector_client.Stop() | 270 self._remote_inspector_client.Stop() |
123 logging.info('Connection to remote inspector terminated.') | 271 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 | 272 |
129 # Must be done at end of this function except for post-cleaning after | 273 # Must be done at end of this function except for post-cleaning after |
130 # Chrome finishes. | 274 # Chrome finishes. |
131 perf.BasePerfTest.tearDown(self) | 275 perf.BasePerfTest.tearDown(self) |
132 | 276 |
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() | 277 # Must be done after perf.BasePerfTest.tearDown() |
139 self._StopReplayServerIfNecessary() | 278 self._StopReplayServerIfNecessary() |
279 if self._dmprof: | |
280 self._dmprof.TearDown() | |
140 | 281 |
141 def _GetArchiveName(self): | 282 def _GetArchiveName(self): |
142 """Return the Web Page Replay archive name that corresponds to a test. | 283 """Return the Web Page Replay archive name that corresponds to a test. |
143 | 284 |
144 Override this function to return the name of an archive that | 285 Override this function to return the name of an archive that |
145 corresponds to the test, e.g "ChromeEndureGmailTest.wpr". | 286 corresponds to the test, e.g "ChromeEndureGmailTest.wpr". |
146 | 287 |
147 Returns: | 288 Returns: |
148 None, by default no archive name is provided. | 289 None, by default no archive name is provided. |
149 """ | 290 """ |
(...skipping 23 matching lines...) Expand all Loading... | |
173 '_GetArchiveName() in your test?') | 314 '_GetArchiveName() in your test?') |
174 else: | 315 else: |
175 if self._archive_path and os.path.exists(self._archive_path): | 316 if self._archive_path and os.path.exists(self._archive_path): |
176 self._use_wpr = True | 317 self._use_wpr = True |
177 else: | 318 else: |
178 self._use_wpr = False | 319 self._use_wpr = False |
179 logging.info( | 320 logging.info( |
180 'Skipping Web Page Replay since archive file %sdoes not exist.', | 321 'Skipping Web Page Replay since archive file %sdoes not exist.', |
181 self._archive_path + ' ' if self._archive_path else '') | 322 self._archive_path + ' ' if self._archive_path else '') |
182 | 323 |
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): | 324 def ExtraChromeFlags(self): |
208 """Ensures Chrome is launched with custom flags. | 325 """Ensures Chrome is launched with custom flags. |
209 | 326 |
210 Returns: | 327 Returns: |
211 A list of extra flags to pass to Chrome when it is launched. | 328 A list of extra flags to pass to Chrome when it is launched. |
212 """ | 329 """ |
213 # The same with setUp, but need to fetch the environment variable since | 330 # The same with setUp, but need to fetch the environment variable since |
214 # ExtraChromeFlags is called before setUp. | 331 # ExtraChromeFlags is called before setUp. |
215 deep_memory_profile = self._GetDeepMemoryProfileEnv( | 332 deep_memory_profile = DeepMemoryProfiler.GetEnvironmentVariable( |
216 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 333 'DEEP_MEMORY_PROFILE', bool, DeepMemoryProfiler.DEEP_MEMORY_PROFILE) |
217 | 334 |
218 # Ensure Chrome enables remote debugging on port 9222. This is required to | 335 # Ensure Chrome enables remote debugging on port 9222. This is required to |
219 # interact with Chrome's remote inspector. | 336 # interact with Chrome's remote inspector. |
220 extra_flags = ['--remote-debugging-port=9222'] | 337 extra_flags = ['--remote-debugging-port=9222'] |
221 if deep_memory_profile: | 338 if deep_memory_profile: |
222 extra_flags.append('--no-sandbox') | 339 extra_flags.append('--no-sandbox') |
223 if self._use_wpr: | 340 if self._use_wpr: |
224 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) | 341 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) |
225 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags | 342 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags |
226 | 343 |
(...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 | 378 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 | 379 performed by this test. The callable is invoked iteratively for the |
263 duration of the test. | 380 duration of the test. |
264 frame_xpath: The string xpath of the frame in which to inject javascript | 381 frame_xpath: The string xpath of the frame in which to inject javascript |
265 to clear chromedriver's cache (a temporary workaround until the | 382 to clear chromedriver's cache (a temporary workaround until the |
266 WebDriver team changes how they handle their DOM node cache). | 383 WebDriver team changes how they handle their DOM node cache). |
267 """ | 384 """ |
268 self._num_errors = 0 | 385 self._num_errors = 0 |
269 self._test_start_time = time.time() | 386 self._test_start_time = time.time() |
270 last_perf_stats_time = time.time() | 387 last_perf_stats_time = time.time() |
271 if self._deep_memory_profile: | 388 if self._dmprof: |
272 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') | 389 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') |
273 self._GetPerformanceStats( | 390 self._GetPerformanceStats( |
274 webapp_name, test_description, tab_title_substring) | 391 webapp_name, test_description, tab_title_substring) |
275 self._iteration_num = 0 # Available to |do_scenario| if needed. | 392 self._iteration_num = 0 # Available to |do_scenario| if needed. |
276 | 393 |
277 self._remote_inspector_client.StartTimelineEventMonitoring( | 394 self._remote_inspector_client.StartTimelineEventMonitoring( |
278 self._OnTimelineEvent) | 395 self._OnTimelineEvent) |
279 | 396 |
280 while time.time() - self._test_start_time < self._test_length_sec: | 397 while time.time() - self._test_start_time < self._test_length_sec: |
281 self._iteration_num += 1 | 398 self._iteration_num += 1 |
282 | 399 |
283 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: | 400 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: |
284 logging.error('Error count threshold (%d) reached. Terminating test ' | 401 logging.error('Error count threshold (%d) reached. Terminating test ' |
285 'early.' % self._ERROR_COUNT_THRESHOLD) | 402 'early.' % self._ERROR_COUNT_THRESHOLD) |
286 break | 403 break |
287 | 404 |
288 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: | 405 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: |
289 last_perf_stats_time = time.time() | 406 last_perf_stats_time = time.time() |
290 if self._deep_memory_profile: | 407 if self._dmprof: |
291 self.HeapProfilerDump('renderer', 'Chrome Endure') | 408 self.HeapProfilerDump('renderer', 'Chrome Endure') |
292 self._GetPerformanceStats( | 409 self._GetPerformanceStats( |
293 webapp_name, test_description, tab_title_substring) | 410 webapp_name, test_description, tab_title_substring) |
294 | 411 |
295 if self._iteration_num % 10 == 0: | 412 if self._iteration_num % 10 == 0: |
296 remaining_time = self._test_length_sec - (time.time() - | 413 remaining_time = self._test_length_sec - (time.time() - |
297 self._test_start_time) | 414 self._test_start_time) |
298 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % | 415 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % |
299 (self._iteration_num, remaining_time)) | 416 (self._iteration_num, remaining_time)) |
300 | 417 |
301 do_scenario() | 418 do_scenario() |
302 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the | 419 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the |
303 # results of Chrome Endure. | 420 # results of Chrome Endure. |
304 # TODO(dennisjeffrey): Once the WebDriver team implements changes to | 421 # TODO(dennisjeffrey): Once the WebDriver team implements changes to |
305 # handle their DOM node cache differently, we need to revisit this. It | 422 # 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. | 423 # may no longer be necessary at that point to forcefully delete the cache. |
307 # Additionally, the Javascript below relies on an internal property of | 424 # Additionally, the Javascript below relies on an internal property of |
308 # WebDriver that may change at any time. This is only a temporary | 425 # WebDriver that may change at any time. This is only a temporary |
309 # workaround to stabilize the Chrome Endure test results. | 426 # workaround to stabilize the Chrome Endure test results. |
310 js = """ | 427 js = """ |
311 (function() { | 428 (function() { |
312 delete document.$wdc_; | 429 delete document.$wdc_; |
313 window.domAutomationController.send('done'); | 430 window.domAutomationController.send('done'); |
314 })(); | 431 })(); |
315 """ | 432 """ |
316 self.ExecuteJavascript(js, frame_xpath=frame_xpath) | 433 self.ExecuteJavascript(js, frame_xpath=frame_xpath) |
317 | 434 |
318 self._remote_inspector_client.StopTimelineEventMonitoring() | 435 self._remote_inspector_client.StopTimelineEventMonitoring() |
319 | 436 |
320 if self._deep_memory_profile: | 437 if self._dmprof: |
321 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') | 438 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') |
322 self._GetPerformanceStats( | 439 self._GetPerformanceStats( |
323 webapp_name, test_description, tab_title_substring, is_last=True) | 440 webapp_name, test_description, tab_title_substring, is_last=True) |
324 | 441 |
325 def _GetProcessInfo(self, tab_title_substring): | 442 def _GetProcessInfo(self, tab_title_substring): |
326 """Gets process info associated with an open browser/tab. | 443 """Gets process info associated with an open browser/tab. |
327 | 444 |
328 Args: | 445 Args: |
329 tab_title_substring: A unique substring contained within the title of | 446 tab_title_substring: A unique substring contained within the title of |
330 the tab to use; needed for locating the tab info. | 447 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. | 508 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 | 509 is_last: A boolean value which should be True if it's the last call of |
393 _GetPerformanceStats. The default is False. | 510 _GetPerformanceStats. The default is False. |
394 """ | 511 """ |
395 logging.info('Gathering performance stats...') | 512 logging.info('Gathering performance stats...') |
396 elapsed_time = int(round(time.time() - self._test_start_time)) | 513 elapsed_time = int(round(time.time() - self._test_start_time)) |
397 | 514 |
398 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() | 515 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() |
399 proc_info = self._GetProcessInfo(tab_title_substring) | 516 proc_info = self._GetProcessInfo(tab_title_substring) |
400 | 517 |
401 # Run Deep Memory Profiler in background. | 518 if self._dmprof: |
402 if self._deep_memory_profile: | 519 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 | 520 |
449 # DOM node count. | 521 # DOM node count. |
450 dom_node_count = memory_counts['DOMNodeCount'] | 522 dom_node_count = memory_counts['DOMNodeCount'] |
451 self._OutputPerfGraphValue( | 523 self._OutputPerfGraphValue( |
452 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', | 524 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', |
453 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), | 525 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), |
454 units_x='seconds') | 526 units_x='seconds') |
455 | 527 |
456 # Event listener count. | 528 # Event listener count. |
457 event_listener_count = memory_counts['EventListenerCount'] | 529 event_listener_count = memory_counts['EventListenerCount'] |
(...skipping 25 matching lines...) Expand all Loading... | |
483 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), | 555 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), |
484 units_x='seconds') | 556 units_x='seconds') |
485 | 557 |
486 # V8 memory allocated. | 558 # V8 memory allocated. |
487 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. | 559 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. |
488 self._OutputPerfGraphValue( | 560 self._OutputPerfGraphValue( |
489 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', | 561 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', |
490 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), | 562 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), |
491 units_x='seconds') | 563 units_x='seconds') |
492 | 564 |
493 # Deep Memory Profiler result. | 565 if self._dmprof: |
494 if self._deep_memory_profile: | 566 self._dmprof.ParseResultAndOutputPerfGraphValues( |
495 deep_memory_profile_results = {} | 567 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 | 568 |
515 logging.info(' Total DOM node count: %d nodes' % dom_node_count) | 569 logging.info(' Total DOM node count: %d nodes' % dom_node_count) |
516 logging.info(' Event listener count: %d listeners' % event_listener_count) | 570 logging.info(' Event listener count: %d listeners' % event_listener_count) |
517 logging.info(' Browser process private memory: %d KB' % | 571 logging.info(' Browser process private memory: %d KB' % |
518 proc_info['browser_private_mem']) | 572 proc_info['browser_private_mem']) |
519 logging.info(' Tab process private memory: %d KB' % | 573 logging.info(' Tab process private memory: %d KB' % |
520 proc_info['tab_private_mem']) | 574 proc_info['tab_private_mem']) |
521 logging.info(' V8 memory used: %f KB' % v8_mem_used) | 575 logging.info(' V8 memory used: %f KB' % v8_mem_used) |
522 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) | 576 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) |
523 | 577 |
(...skipping 718 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1242 if not os.path.exists(scripts): | 1296 if not os.path.exists(scripts): |
1243 raise IOError('Injected scripts %s not found.' % scripts) | 1297 raise IOError('Injected scripts %s not found.' % scripts) |
1244 replay_options = ['--inject_scripts', scripts] | 1298 replay_options = ['--inject_scripts', scripts] |
1245 if 'WPR_RECORD' in os.environ: | 1299 if 'WPR_RECORD' in os.environ: |
1246 replay_options.append('--append') | 1300 replay_options.append('--append') |
1247 return webpagereplay.ReplayServer(archive_path, replay_options) | 1301 return webpagereplay.ReplayServer(archive_path, replay_options) |
1248 | 1302 |
1249 | 1303 |
1250 if __name__ == '__main__': | 1304 if __name__ == '__main__': |
1251 pyauto_functional.Main() | 1305 pyauto_functional.Main() |
OLD | NEW |