Chromium Code Reviews| 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 |