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 19 matching lines...) Expand all Loading... | |
30 import pyauto_utils | 30 import pyauto_utils |
31 import remote_inspector_client | 31 import remote_inspector_client |
32 import selenium.common.exceptions | 32 import selenium.common.exceptions |
33 from selenium.webdriver.support.ui import WebDriverWait | 33 from selenium.webdriver.support.ui import WebDriverWait |
34 | 34 |
35 | 35 |
36 class NotSupportedEnvironmentError(RuntimeError): | 36 class NotSupportedEnvironmentError(RuntimeError): |
37 """Represent an error raised since the environment (OS) is not supported.""" | 37 """Represent an error raised since the environment (OS) is not supported.""" |
38 pass | 38 pass |
39 | 39 |
40 | |
41 class DeepMemoryProfiler(object): | |
dennis_jeffrey
2012/09/05 17:34:12
add a docstring for this class
Dai Mikurube (NOT FULLTIME)
2012/09/06 05:30:10
Done.
| |
42 DEEP_MEMORY_PROFILE = False | |
43 DEEP_MEMORY_PROFILE_SAVE = False | |
44 | |
45 _DMPROF_DIR_PATH = os.path.join( | |
46 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, | |
47 'tools', 'deep_memory_profiler') | |
48 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') | |
49 | |
50 def __init__(self): | |
51 self._enabled = self.GetDeepMemoryProfileEnv( | |
52 'DEEP_MEMORY_PROFILE', bool, self.DEEP_MEMORY_PROFILE) | |
53 self._save = False | |
54 self._json_file = None | |
55 self._last_json_filename = '' | |
56 self._proc = None | |
57 self._last_time = -1.0 | |
58 | |
59 def SetUp(self, is_linux): | |
60 if self._enabled: | |
dennis_jeffrey
2012/09/05 17:34:12
Rather than adding the "is enabled" checks at the
Dai Mikurube (NOT FULLTIME)
2012/09/06 05:30:10
Added checks at the call sites, but kept DeepMemor
| |
61 if not is_linux: | |
62 raise NotSupportedEnvironmentError( | |
63 'Deep Memory Profiler is not supported in this environment (OS).') | |
64 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') | |
65 self._tempdir = tempfile.mkdtemp(prefix=dir_prefix) | |
66 os.environ['HEAPPROFILE'] = os.path.join(self._tempdir, 'endure') | |
67 os.environ['HEAP_PROFILE_MMAP'] = '1' | |
68 os.environ['DEEP_HEAP_PROFILE'] = '1' | |
69 self._save = self.GetDeepMemoryProfileEnv( | |
70 'DEEP_MEMORY_PROFILE_SAVE', bool, self.DEEP_MEMORY_PROFILE_SAVE) | |
71 | |
72 def TearDown(self): | |
73 if self._enabled: | |
74 del os.environ['DEEP_HEAP_PROFILE'] | |
75 del os.environ['HEAP_PROFILE_MMAP'] | |
76 del os.environ['HEAPPROFILE'] | |
77 | |
78 def RemoveTemporaryFiles(self): | |
79 """Remove the temporary directory after Chrome finishes.""" | |
80 if self._enabled and not self._save and self._tempdir: | |
81 pyauto_utils.RemovePath(self._tempdir) | |
82 | |
83 def IsEnabled(self): | |
84 return self._enabled | |
85 | |
86 def LogFirstMessage(self): | |
87 if self._enabled: | |
88 logging.info('Running with the Deep Memory Profiler.') | |
89 if self._save: | |
90 logging.info(' Dumped files won\'t be cleaned.') | |
91 else: | |
92 logging.info(' Dumped files will be cleaned up after every test.') | |
93 | |
94 def GetDeepMemoryProfileEnv(self, env_name, converter, default): | |
95 """Returns a converted environment variable for the Deep Memory Profiler. | |
96 | |
97 Args: | |
98 env_name: A string name of an environment variable. | |
99 converter: A function taking a string to convert an environment variable. | |
100 default: A value used if the environment variable is not specified. | |
101 | |
102 Returns: | |
103 A value converted from the environment variable with 'converter'. | |
104 """ | |
105 return converter(os.environ.get(env_name, default)) | |
106 | |
107 def StartProfiler(self, proc_info, is_last): | |
108 """Starts Deep Memory Profiler in background.""" | |
109 if self._enabled: | |
110 logging.info(' Profiling with the Deep Memory Profiler...') | |
111 | |
112 # Wait for a running dmprof process for last _GetPerformanceStat call to | |
113 # cover last dump files. | |
114 if is_last: | |
115 logging.info(' Waiting for the last dmprof.') | |
116 self._WaitForDeepMemoryProfiler() | |
117 | |
118 if self._proc and self._proc.poll() is None: | |
119 logging.info(' Last dmprof is still running.') | |
120 else: | |
121 if self._json_file: | |
122 self._last_json_filename = self._json_file.name | |
123 self._json_file.close() | |
124 self._json_file = None | |
125 first_dump = '' | |
126 last_dump = '' | |
127 for filename in sorted(os.listdir(self._tempdir)): | |
128 if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'], | |
129 filename): | |
130 logging.info(' Profiled dump file: %s' % filename) | |
131 last_dump = filename | |
132 if not first_dump: | |
133 first_dump = filename | |
134 if first_dump: | |
135 logging.info(' First dump file: %s' % first_dump) | |
136 matched = re.match('^endure.\d+.(\d+).heap$', last_dump) | |
137 last_sequence_id = matched.group(1) | |
138 self._json_file = open( | |
139 os.path.join(self._tempdir, | |
140 'endure.%05d.%s.json' % (proc_info['tab_pid'], | |
141 last_sequence_id)), 'w+') | |
142 self._proc = subprocess.Popen( | |
143 '%s json %s' % (self._DMPROF_SCRIPT_PATH, | |
144 os.path.join(self._tempdir, first_dump)), | |
145 shell=True, stdout=self._json_file) | |
146 # Don't wait for the new process since dmprof may take long time. | |
147 | |
dennis_jeffrey
2012/09/05 17:34:12
remove this blank line; the comment above goes wit
Dai Mikurube (NOT FULLTIME)
2012/09/06 05:30:10
Done.
| |
148 if is_last: | |
149 self._WaitForDeepMemoryProfiler() | |
150 | |
151 else: | |
152 logging.info(' No dump files.') | |
153 | |
154 def ParseResultAndOutputPerfGraphValues( | |
155 self, webapp_name, test_description, output_perf_graph_value): | |
156 """Parses Deep Memory Profiler result, and outputs perf graph values.""" | |
157 if self._enabled: | |
158 results = {} | |
159 if self._last_json_filename: | |
160 json_data = {} | |
161 with open(self._last_json_filename) as json_f: | |
162 json_data = json.load(json_f) | |
163 if json_data['version'] == 'JSON_DEEP_1': | |
164 results = json_data['snapshots'] | |
165 elif json_data['version'] == 'JSON_DEEP_2': | |
166 results = json_data['policies']['l2']['snapshots'] | |
167 if results and results[-1]['second'] > self._last_time: | |
168 started = False | |
169 for legend in json_data['policies']['l2']['legends']: | |
170 if legend == 'FROM_HERE_FOR_TOTAL': | |
171 started = True | |
172 elif legend == 'UNTIL_HERE_FOR_TOTAL': | |
173 break | |
174 elif started: | |
175 output_perf_graph_value( | |
176 legend.encode('utf-8'), [ | |
177 (int(round(snapshot['second'])), snapshot[legend] / 1024) | |
178 for snapshot in results | |
179 if snapshot['second'] > self._last_time], | |
180 'KB', | |
181 graph_name='%s%s-DMP' % (webapp_name, test_description), | |
182 units_x='seconds', is_stacked=True) | |
183 self._last_time = results[-1]['second'] | |
184 | |
185 def _WaitForDeepMemoryProfiler(self): | |
186 """Waits for the Deep Memory Profiler to finish if running.""" | |
187 if self._enabled and self._proc: | |
188 self._proc.wait() | |
189 self._proc = None | |
190 if self._json_file: | |
191 self._last_json_filename = self._json_file.name | |
192 self._json_file.close() | |
193 self._json_file = None | |
194 | |
195 | |
40 class ChromeEndureBaseTest(perf.BasePerfTest): | 196 class ChromeEndureBaseTest(perf.BasePerfTest): |
41 """Implements common functionality for all Chrome Endure tests. | 197 """Implements common functionality for all Chrome Endure tests. |
42 | 198 |
43 All Chrome Endure test classes should inherit from this class. | 199 All Chrome Endure test classes should inherit from this class. |
44 """ | 200 """ |
45 | 201 |
46 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. | 202 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. |
47 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. | 203 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
48 # TODO(dennisjeffrey): Do we still need to tolerate errors? | 204 # TODO(dennisjeffrey): Do we still need to tolerate errors? |
49 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. | 205 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. |
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 | |
57 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') | |
58 | 206 |
59 def setUp(self): | 207 def setUp(self): |
60 # The environment variables for the Deep Memory Profiler must be set | 208 # The environment variables for the Deep Memory Profiler must be set |
61 # before perf.BasePerfTest.setUp() to inherit them to Chrome. | 209 # before perf.BasePerfTest.setUp() to inherit them to Chrome. |
62 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( | 210 self._dmprof = DeepMemoryProfiler() |
63 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 211 self._dmprof.SetUp(self.IsLinux()) |
64 | |
65 if self._deep_memory_profile: | |
66 if not self.IsLinux(): | |
67 raise NotSupportedEnvironmentError( | |
68 'Deep Memory Profiler is not supported in this environment (OS).') | |
69 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') | |
70 self._deep_tempdir = tempfile.mkdtemp(prefix=dir_prefix) | |
71 os.environ['HEAPPROFILE'] = os.path.join(self._deep_tempdir, 'endure') | |
72 os.environ['HEAP_PROFILE_MMAP'] = 'True' | |
73 os.environ['DEEP_HEAP_PROFILE'] = 'True' | |
74 | 212 |
75 perf.BasePerfTest.setUp(self) | 213 perf.BasePerfTest.setUp(self) |
76 | 214 |
77 self._test_length_sec = int( | 215 self._test_length_sec = int( |
78 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) | 216 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) |
79 self._get_perf_stats_interval = int( | 217 self._get_perf_stats_interval = int( |
80 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) | 218 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) |
81 | 219 |
82 self._deep_memory_profile_save = self._GetDeepMemoryProfileEnv( | |
83 'DEEP_MEMORY_PROFILE_SAVE', bool, self._DEEP_MEMORY_PROFILE_SAVE) | |
84 | |
85 logging.info('Running test for %d seconds.', self._test_length_sec) | 220 logging.info('Running test for %d seconds.', self._test_length_sec) |
86 logging.info('Gathering perf stats every %d seconds.', | 221 logging.info('Gathering perf stats every %d seconds.', |
87 self._get_perf_stats_interval) | 222 self._get_perf_stats_interval) |
88 if self._deep_memory_profile: | 223 |
89 logging.info('Running with the Deep Memory Profiler.') | 224 self._dmprof.LogFirstMessage() |
90 if self._deep_memory_profile_save: | |
91 logging.info(' Dumped files won\'t be cleaned.') | |
92 else: | |
93 logging.info(' Dumped files will be cleaned up after every test.') | |
94 | 225 |
95 # Set up a remote inspector client associated with tab 0. | 226 # Set up a remote inspector client associated with tab 0. |
96 logging.info('Setting up connection to remote inspector...') | 227 logging.info('Setting up connection to remote inspector...') |
97 self._remote_inspector_client = ( | 228 self._remote_inspector_client = ( |
98 remote_inspector_client.RemoteInspectorClient()) | 229 remote_inspector_client.RemoteInspectorClient()) |
99 logging.info('Connection to remote inspector set up successfully.') | 230 logging.info('Connection to remote inspector set up successfully.') |
100 | 231 |
101 self._test_start_time = 0 | 232 self._test_start_time = 0 |
102 self._num_errors = 0 | 233 self._num_errors = 0 |
103 self._events_to_output = [] | 234 self._events_to_output = [] |
104 self._deep_memory_profile_json_file = None | |
105 self._deep_memory_profile_last_json_filename = '' | |
106 self._deep_memory_profile_proc = None | |
107 | 235 |
108 def tearDown(self): | 236 def tearDown(self): |
109 logging.info('Terminating connection to remote inspector...') | 237 logging.info('Terminating connection to remote inspector...') |
110 self._remote_inspector_client.Stop() | 238 self._remote_inspector_client.Stop() |
111 logging.info('Connection to remote inspector terminated.') | 239 logging.info('Connection to remote inspector terminated.') |
112 if self._deep_memory_profile: | 240 |
113 del os.environ['DEEP_HEAP_PROFILE'] | 241 self._dmprof.TearDown() |
114 del os.environ['HEAP_PROFILE_MMAP'] | |
115 del os.environ['HEAPPROFILE'] | |
116 | 242 |
117 # Must be done at end of this function except for post-cleaning after | 243 # Must be done at end of this function except for post-cleaning after |
118 # Chrome finishes. | 244 # Chrome finishes. |
119 perf.BasePerfTest.tearDown(self) | 245 perf.BasePerfTest.tearDown(self) |
120 | 246 |
121 # Remove the temporary directory after Chrome finishes in tearDown. | 247 self._dmprof.RemoveTemporaryFiles() |
dennis_jeffrey
2012/09/05 17:34:12
is there a reason this is done after calling the b
Dai Mikurube (NOT FULLTIME)
2012/09/06 05:30:10
It must be called after Chrome finishes. Chrome m
| |
122 if (self._deep_memory_profile and | |
123 not self._deep_memory_profile_save and | |
124 self._deep_tempdir): | |
125 pyauto_utils.RemovePath(self._deep_tempdir) | |
126 | |
127 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): | |
128 """Returns a converted environment variable for the Deep Memory Profiler. | |
129 | |
130 Args: | |
131 env_name: A string name of an environment variable. | |
132 converter: A function taking a string to convert an environment variable. | |
133 default: A value used if the environment variable is not specified. | |
134 | |
135 Returns: | |
136 A value converted from the environment variable with 'converter'. | |
137 """ | |
138 return converter(os.environ.get(env_name, default)) | |
139 | |
140 def _WaitForDeepMemoryProfiler(self): | |
141 """Waits for the Deep Memory Profiler to finish if running.""" | |
142 if self._deep_memory_profile and self._deep_memory_profile_proc: | |
143 self._deep_memory_profile_proc.wait() | |
144 self._deep_memory_profile_proc = None | |
145 if self._deep_memory_profile_json_file: | |
146 self._deep_memory_profile_last_json_filename = ( | |
147 self._deep_memory_profile_json_file.name) | |
148 self._deep_memory_profile_json_file.close() | |
149 self._deep_memory_profile_json_file = None | |
150 | 248 |
151 def ExtraChromeFlags(self): | 249 def ExtraChromeFlags(self): |
152 """Ensures Chrome is launched with custom flags. | 250 """Ensures Chrome is launched with custom flags. |
153 | 251 |
154 Returns: | 252 Returns: |
155 A list of extra flags to pass to Chrome when it is launched. | 253 A list of extra flags to pass to Chrome when it is launched. |
156 """ | 254 """ |
157 # The same with setUp, but need to fetch the environment variable since | 255 # The same with setUp, but need to fetch the environment variable since |
158 # ExtraChromeFlags is called before setUp. | 256 # ExtraChromeFlags is called before setUp. |
159 deep_memory_profile = self._GetDeepMemoryProfileEnv( | 257 deep_memory_profile = self._dmprof.GetDeepMemoryProfileEnv( |
160 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 258 'DEEP_MEMORY_PROFILE', bool, self._dmprof.DEEP_MEMORY_PROFILE) |
161 | 259 |
162 # Ensure Chrome enables remote debugging on port 9222. This is required to | 260 # Ensure Chrome enables remote debugging on port 9222. This is required to |
163 # interact with Chrome's remote inspector. | 261 # interact with Chrome's remote inspector. |
164 extra_flags = ['--remote-debugging-port=9222'] | 262 extra_flags = ['--remote-debugging-port=9222'] |
165 if deep_memory_profile: | 263 if deep_memory_profile: |
166 extra_flags.append('--no-sandbox') | 264 extra_flags.append('--no-sandbox') |
167 return (perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags) | 265 return (perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags) |
168 | 266 |
169 def _OnTimelineEvent(self, event_info): | 267 def _OnTimelineEvent(self, event_info): |
170 """Invoked by the Remote Inspector Client when a timeline event occurs. | 268 """Invoked by the Remote Inspector Client when a timeline event occurs. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 do_scenario: A callable to be invoked that implements the scenario to be | 301 do_scenario: A callable to be invoked that implements the scenario to be |
204 performed by this test. The callable is invoked iteratively for the | 302 performed by this test. The callable is invoked iteratively for the |
205 duration of the test. | 303 duration of the test. |
206 frame_xpath: The string xpath of the frame in which to inject javascript | 304 frame_xpath: The string xpath of the frame in which to inject javascript |
207 to clear chromedriver's cache (a temporary workaround until the | 305 to clear chromedriver's cache (a temporary workaround until the |
208 WebDriver team changes how they handle their DOM node cache). | 306 WebDriver team changes how they handle their DOM node cache). |
209 """ | 307 """ |
210 self._num_errors = 0 | 308 self._num_errors = 0 |
211 self._test_start_time = time.time() | 309 self._test_start_time = time.time() |
212 last_perf_stats_time = time.time() | 310 last_perf_stats_time = time.time() |
213 if self._deep_memory_profile: | 311 if self._dmprof.IsEnabled(): |
214 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') | 312 self.HeapProfilerDump('renderer', 'Chrome Endure (first)') |
215 self._GetPerformanceStats( | 313 self._GetPerformanceStats( |
216 webapp_name, test_description, tab_title_substring) | 314 webapp_name, test_description, tab_title_substring) |
217 self._iteration_num = 0 # Available to |do_scenario| if needed. | 315 self._iteration_num = 0 # Available to |do_scenario| if needed. |
218 | 316 |
219 self._remote_inspector_client.StartTimelineEventMonitoring( | 317 self._remote_inspector_client.StartTimelineEventMonitoring( |
220 self._OnTimelineEvent) | 318 self._OnTimelineEvent) |
221 | 319 |
222 while time.time() - self._test_start_time < self._test_length_sec: | 320 while time.time() - self._test_start_time < self._test_length_sec: |
223 self._iteration_num += 1 | 321 self._iteration_num += 1 |
224 | 322 |
225 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: | 323 if self._num_errors >= self._ERROR_COUNT_THRESHOLD: |
226 logging.error('Error count threshold (%d) reached. Terminating test ' | 324 logging.error('Error count threshold (%d) reached. Terminating test ' |
227 'early.' % self._ERROR_COUNT_THRESHOLD) | 325 'early.' % self._ERROR_COUNT_THRESHOLD) |
228 break | 326 break |
229 | 327 |
230 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: | 328 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: |
231 last_perf_stats_time = time.time() | 329 last_perf_stats_time = time.time() |
232 if self._deep_memory_profile: | 330 if self._dmprof.IsEnabled(): |
233 self.HeapProfilerDump('renderer', 'Chrome Endure') | 331 self.HeapProfilerDump('renderer', 'Chrome Endure') |
234 self._GetPerformanceStats( | 332 self._GetPerformanceStats( |
235 webapp_name, test_description, tab_title_substring) | 333 webapp_name, test_description, tab_title_substring) |
236 | 334 |
237 if self._iteration_num % 10 == 0: | 335 if self._iteration_num % 10 == 0: |
238 remaining_time = self._test_length_sec - (time.time() - | 336 remaining_time = self._test_length_sec - (time.time() - |
239 self._test_start_time) | 337 self._test_start_time) |
240 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % | 338 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % |
241 (self._iteration_num, remaining_time)) | 339 (self._iteration_num, remaining_time)) |
242 | 340 |
243 do_scenario() | 341 do_scenario() |
244 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the | 342 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the |
245 # results of Chrome Endure. | 343 # results of Chrome Endure. |
246 # TODO(dennisjeffrey): Once the WebDriver team implements changes to | 344 # TODO(dennisjeffrey): Once the WebDriver team implements changes to |
247 # handle their DOM node cache differently, we need to revisit this. It | 345 # handle their DOM node cache differently, we need to revisit this. It |
248 # may no longer be necessary at that point to forcefully delete the cache. | 346 # may no longer be necessary at that point to forcefully delete the cache. |
249 # Additionally, the Javascript below relies on an internal property of | 347 # Additionally, the Javascript below relies on an internal property of |
250 # WebDriver that may change at any time. This is only a temporary | 348 # WebDriver that may change at any time. This is only a temporary |
251 # workaround to stabilize the Chrome Endure test results. | 349 # workaround to stabilize the Chrome Endure test results. |
252 js = """ | 350 js = """ |
253 (function() { | 351 (function() { |
254 delete document.$wdc_; | 352 delete document.$wdc_; |
255 window.domAutomationController.send('done'); | 353 window.domAutomationController.send('done'); |
256 })(); | 354 })(); |
257 """ | 355 """ |
258 self.ExecuteJavascript(js, frame_xpath=frame_xpath) | 356 self.ExecuteJavascript(js, frame_xpath=frame_xpath) |
259 | 357 |
260 self._remote_inspector_client.StopTimelineEventMonitoring() | 358 self._remote_inspector_client.StopTimelineEventMonitoring() |
261 | 359 |
262 if self._deep_memory_profile: | 360 if self._dmprof.IsEnabled(): |
263 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') | 361 self.HeapProfilerDump('renderer', 'Chrome Endure (last)') |
264 self._GetPerformanceStats( | 362 self._GetPerformanceStats( |
265 webapp_name, test_description, tab_title_substring, is_last=True) | 363 webapp_name, test_description, tab_title_substring, is_last=True) |
266 | 364 |
267 def _GetProcessInfo(self, tab_title_substring): | 365 def _GetProcessInfo(self, tab_title_substring): |
268 """Gets process info associated with an open browser/tab. | 366 """Gets process info associated with an open browser/tab. |
269 | 367 |
270 Args: | 368 Args: |
271 tab_title_substring: A unique substring contained within the title of | 369 tab_title_substring: A unique substring contained within the title of |
272 the tab to use; needed for locating the tab info. | 370 the tab to use; needed for locating the tab info. |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 the tab to use, for identifying the appropriate tab. | 431 the tab to use, for identifying the appropriate tab. |
334 is_last: A boolean value which should be True if it's the last call of | 432 is_last: A boolean value which should be True if it's the last call of |
335 _GetPerformanceStats. The default is False. | 433 _GetPerformanceStats. The default is False. |
336 """ | 434 """ |
337 logging.info('Gathering performance stats...') | 435 logging.info('Gathering performance stats...') |
338 elapsed_time = int(round(time.time() - self._test_start_time)) | 436 elapsed_time = int(round(time.time() - self._test_start_time)) |
339 | 437 |
340 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() | 438 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() |
341 proc_info = self._GetProcessInfo(tab_title_substring) | 439 proc_info = self._GetProcessInfo(tab_title_substring) |
342 | 440 |
343 # Run Deep Memory Profiler in background. | 441 self._dmprof.StartProfiler(proc_info, is_last) |
344 if self._deep_memory_profile: | |
345 logging.info(' Profiling with the Deep Memory Profiler...') | |
346 | |
347 # Wait for a running dmprof process for last _GetPerformanceStat call to | |
348 # cover last dump files. | |
349 if is_last: | |
350 logging.info(' Waiting for the last dmprof.') | |
351 self._WaitForDeepMemoryProfiler() | |
352 | |
353 if (self._deep_memory_profile_proc and | |
354 self._deep_memory_profile_proc.poll() is None): | |
355 logging.info(' Last dmprof is still running.') | |
356 else: | |
357 if self._deep_memory_profile_json_file: | |
358 self._deep_memory_profile_last_json_filename = ( | |
359 self._deep_memory_profile_json_file.name) | |
360 self._deep_memory_profile_json_file.close() | |
361 self._deep_memory_profile_json_file = None | |
362 first_dump = '' | |
363 last_dump = '' | |
364 for filename in sorted(os.listdir(self._deep_tempdir)): | |
365 if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'], | |
366 filename): | |
367 logging.info(' Profiled dump file: %s' % filename) | |
368 last_dump = filename | |
369 if not first_dump: | |
370 first_dump = filename | |
371 if first_dump: | |
372 logging.info(' First dump file: %s' % first_dump) | |
373 matched= re.match('^endure.\d+.(\d+).heap$', last_dump) | |
374 last_sequence_id = matched.group(1) | |
375 self._deep_memory_profile_json_file = open( | |
376 os.path.join(self._deep_tempdir, | |
377 'endure.%05d.%s.json' % (proc_info['tab_pid'], | |
378 last_sequence_id)), 'w+') | |
379 self._deep_memory_profile_proc = subprocess.Popen( | |
380 '%s json %s' % (self._DMPROF_SCRIPT_PATH, | |
381 os.path.join(self._deep_tempdir, first_dump)), | |
382 shell=True, stdout=self._deep_memory_profile_json_file) | |
383 # Don't wait for the new process since dmprof may take long time. | |
384 | |
385 if is_last: | |
386 self._WaitForDeepMemoryProfiler() | |
387 | |
388 else: | |
389 logging.info(' No dump files.') | |
390 | 442 |
391 # DOM node count. | 443 # DOM node count. |
392 dom_node_count = memory_counts['DOMNodeCount'] | 444 dom_node_count = memory_counts['DOMNodeCount'] |
393 self._OutputPerfGraphValue( | 445 self._OutputPerfGraphValue( |
394 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', | 446 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', |
395 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), | 447 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), |
396 units_x='seconds') | 448 units_x='seconds') |
397 | 449 |
398 # Event listener count. | 450 # Event listener count. |
399 event_listener_count = memory_counts['EventListenerCount'] | 451 event_listener_count = memory_counts['EventListenerCount'] |
(...skipping 25 matching lines...) Expand all Loading... | |
425 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), | 477 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), |
426 units_x='seconds') | 478 units_x='seconds') |
427 | 479 |
428 # V8 memory allocated. | 480 # V8 memory allocated. |
429 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. | 481 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. |
430 self._OutputPerfGraphValue( | 482 self._OutputPerfGraphValue( |
431 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', | 483 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', |
432 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), | 484 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), |
433 units_x='seconds') | 485 units_x='seconds') |
434 | 486 |
435 # Deep Memory Profiler result. | 487 self._dmprof.ParseResultAndOutputPerfGraphValues( |
436 if self._deep_memory_profile: | 488 webapp_name, test_description, self._OutputPerfGraphValue) |
437 deep_memory_profile_results = {} | |
438 if self._deep_memory_profile_last_json_filename: | |
439 json_data = {} | |
440 with open(self._deep_memory_profile_last_json_filename) as json_f: | |
441 json_data = json.load(json_f) | |
442 if json_data['version'] == 'JSON_DEEP_1': | |
443 deep_memory_profile_results = json_data['snapshots'] | |
444 elif json_data['version'] == 'JSON_DEEP_2': | |
445 deep_memory_profile_results = json_data['policies']['l0']['snapshots'] | |
446 if deep_memory_profile_results: | |
447 self._OutputPerfGraphValue( | |
448 'DMP-TCMallocUsed', [ | |
449 (snapshot['second'], snapshot['tc-used-all'] / 1024.0) | |
450 for snapshot in deep_memory_profile_results], | |
451 'KB', | |
452 graph_name='%s%s-DMP-TCUsed' % (webapp_name, test_description), | |
453 units_x='seconds') | |
454 # TODO(dmikurube): Output graph values (for multi-lined graphs), here. | |
455 # 'deep_memory_profile_results' is the value to be output. | |
456 | 489 |
457 logging.info(' Total DOM node count: %d nodes' % dom_node_count) | 490 logging.info(' Total DOM node count: %d nodes' % dom_node_count) |
458 logging.info(' Event listener count: %d listeners' % event_listener_count) | 491 logging.info(' Event listener count: %d listeners' % event_listener_count) |
459 logging.info(' Browser process private memory: %d KB' % | 492 logging.info(' Browser process private memory: %d KB' % |
460 proc_info['browser_private_mem']) | 493 proc_info['browser_private_mem']) |
461 logging.info(' Tab process private memory: %d KB' % | 494 logging.info(' Tab process private memory: %d KB' % |
462 proc_info['tab_private_mem']) | 495 proc_info['tab_private_mem']) |
463 logging.info(' V8 memory used: %f KB' % v8_mem_used) | 496 logging.info(' V8 memory used: %f KB' % v8_mem_used) |
464 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) | 497 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) |
465 | 498 |
(...skipping 657 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1123 self._num_errors += 1 | 1156 self._num_errors += 1 |
1124 | 1157 |
1125 time.sleep(1) | 1158 time.sleep(1) |
1126 | 1159 |
1127 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, | 1160 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, |
1128 test_description, scenario) | 1161 test_description, scenario) |
1129 | 1162 |
1130 | 1163 |
1131 if __name__ == '__main__': | 1164 if __name__ == '__main__': |
1132 pyauto_functional.Main() | 1165 pyauto_functional.Main() |
OLD | NEW |