browser.py revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1# Copyright (c) 2012 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import os 6 7from telemetry.core import browser_credentials 8from telemetry.core import extension_dict 9from telemetry.core import platform 10from telemetry.core import tab_list 11from telemetry.core import temporary_http_server 12from telemetry.core import wpr_modes 13from telemetry.core import wpr_server 14from telemetry.core.chrome import browser_backend 15from telemetry.core.platform.profiler import profiler_finder 16 17class Browser(object): 18 """A running browser instance that can be controlled in a limited way. 19 20 To create a browser instance, use browser_finder.FindBrowser. 21 22 Be sure to clean up after yourself by calling Close() when you are done with 23 the browser. Or better yet: 24 browser_to_create = FindBrowser(options) 25 with browser_to_create.Create() as browser: 26 ... do all your operations on browser here 27 """ 28 def __init__(self, backend, platform_backend): 29 self._browser_backend = backend 30 self._http_server = None 31 self._wpr_server = None 32 self._platform = platform.Platform(platform_backend) 33 self._platform_backend = platform_backend 34 self._tabs = tab_list.TabList(backend.tab_list_backend) 35 self._extensions = None 36 if backend.supports_extensions: 37 self._extensions = extension_dict.ExtensionDict( 38 backend.extension_dict_backend) 39 self.credentials = browser_credentials.BrowserCredentials() 40 self._platform.SetFullPerformanceModeEnabled(True) 41 self._active_profilers = [] 42 43 def __enter__(self): 44 return self 45 46 def __exit__(self, *args): 47 self.Close() 48 49 @property 50 def platform(self): 51 return self._platform 52 53 @property 54 def browser_type(self): 55 return self._browser_backend.browser_type 56 57 @property 58 def is_content_shell(self): 59 """Returns whether this browser is a content shell, only.""" 60 return self._browser_backend.is_content_shell 61 62 @property 63 def supports_extensions(self): 64 return self._browser_backend.supports_extensions 65 66 @property 67 def supports_tab_control(self): 68 return self._browser_backend.supports_tab_control 69 70 @property 71 def tabs(self): 72 return self._tabs 73 74 @property 75 def extensions(self): 76 """Returns the extension dictionary if it exists.""" 77 if not self.supports_extensions: 78 raise browser_backend.ExtensionsNotSupportedException( 79 'Extensions not supported') 80 return self._extensions 81 82 @property 83 def supports_tracing(self): 84 return self._browser_backend.supports_tracing 85 86 def is_profiler_active(self, profiler_name): 87 return profiler_name in [profiler.name() for 88 profiler in self._active_profilers] 89 90 def _GetStatsCommon(self, pid_stats_function): 91 browser_pid = self._browser_backend.pid 92 result = { 93 'Browser': dict(pid_stats_function(browser_pid), **{'ProcessCount': 1}), 94 'Renderer': {'ProcessCount': 0}, 95 'Gpu': {'ProcessCount': 0} 96 } 97 child_process_count = 0 98 for child_pid in self._platform_backend.GetChildPids(browser_pid): 99 child_process_count += 1 100 # Process type detection is causing exceptions. 101 # http://crbug.com/240951 102 try: 103 child_cmd_line = self._platform_backend.GetCommandLine(child_pid) 104 child_process_name = self._browser_backend.GetProcessName( 105 child_cmd_line) 106 except Exception: 107 child_process_name = 'renderer' 108 process_name_type_key_map = {'gpu-process': 'Gpu', 'renderer': 'Renderer'} 109 if child_process_name in process_name_type_key_map: 110 child_process_type_key = process_name_type_key_map[child_process_name] 111 else: 112 # TODO: identify other process types (zygote, plugin, etc), instead of 113 # lumping them in with renderer processes. 114 child_process_type_key = 'Renderer' 115 child_stats = pid_stats_function(child_pid) 116 result[child_process_type_key]['ProcessCount'] += 1 117 for k, v in child_stats.iteritems(): 118 if k in result[child_process_type_key]: 119 result[child_process_type_key][k] += v 120 else: 121 result[child_process_type_key][k] = v 122 for v in result.itervalues(): 123 if v['ProcessCount'] > 1: 124 for k in v.keys(): 125 if k.endswith('Peak'): 126 del v[k] 127 del v['ProcessCount'] 128 result['ProcessCount'] = child_process_count 129 return result 130 131 @property 132 def memory_stats(self): 133 """Returns a dict of memory statistics for the browser: 134 { 'Browser': { 135 'VM': S, 136 'VMPeak': T, 137 'WorkingSetSize': U, 138 'WorkingSetSizePeak': V, 139 'ProportionalSetSize': W, 140 'PrivateDirty': X 141 }, 142 'Gpu': { 143 'VM': S, 144 'VMPeak': T, 145 'WorkingSetSize': U, 146 'WorkingSetSizePeak': V, 147 'ProportionalSetSize': W, 148 'PrivateDirty': X 149 }, 150 'Renderer': { 151 'VM': S, 152 'VMPeak': T, 153 'WorkingSetSize': U, 154 'WorkingSetSizePeak': V, 155 'ProportionalSetSize': W, 156 'PrivateDirty': X 157 }, 158 'SystemCommitCharge': Y, 159 'ProcessCount': Z, 160 } 161 Any of the above keys may be missing on a per-platform basis. 162 """ 163 result = self._GetStatsCommon(self._platform_backend.GetMemoryStats) 164 result['SystemCommitCharge'] = \ 165 self._platform_backend.GetSystemCommitCharge() 166 return result 167 168 @property 169 def io_stats(self): 170 """Returns a dict of IO statistics for the browser: 171 { 'Browser': { 172 'ReadOperationCount': W, 173 'WriteOperationCount': X, 174 'ReadTransferCount': Y, 175 'WriteTransferCount': Z 176 }, 177 'Gpu': { 178 'ReadOperationCount': W, 179 'WriteOperationCount': X, 180 'ReadTransferCount': Y, 181 'WriteTransferCount': Z 182 }, 183 'Renderer': { 184 'ReadOperationCount': W, 185 'WriteOperationCount': X, 186 'ReadTransferCount': Y, 187 'WriteTransferCount': Z 188 } 189 } 190 """ 191 result = self._GetStatsCommon(self._platform_backend.GetIOStats) 192 del result['ProcessCount'] 193 return result 194 195 def StartProfiling(self, options, base_output_file): 196 """Starts profiling using |options|.profiler_tool. Results are saved to 197 |base_output_file|.<process_name>.""" 198 assert not self._active_profilers 199 200 profiler_class = profiler_finder.FindProfiler(options.profiler_tool) 201 202 if not profiler_class.is_supported(options): 203 raise Exception('The %s profiler is not ' + 204 'supported on this platform.' % options.profiler_tool) 205 206 self._active_profilers.append( 207 profiler_class(self._browser_backend, self._platform_backend, 208 base_output_file)) 209 210 def StopProfiling(self): 211 """Stops all active profilers and saves their results.""" 212 for profiler in self._active_profilers: 213 profiler.CollectProfile() 214 self._active_profilers = [] 215 216 def StartTracing(self, custom_categories=None): 217 return self._browser_backend.StartTracing(custom_categories) 218 219 def StopTracing(self): 220 return self._browser_backend.StopTracing() 221 222 def GetTraceResultAndReset(self): 223 """Returns the result of the trace, as TraceResult object.""" 224 return self._browser_backend.GetTraceResultAndReset() 225 226 def Close(self): 227 """Closes this browser.""" 228 self._platform.SetFullPerformanceModeEnabled(False) 229 if self._wpr_server: 230 self._wpr_server.Close() 231 self._wpr_server = None 232 233 if self._http_server: 234 self._http_server.Close() 235 self._http_server = None 236 237 self._browser_backend.Close() 238 self.credentials = None 239 240 @property 241 def http_server(self): 242 return self._http_server 243 244 def SetHTTPServerDirectories(self, paths): 245 """Returns True if the HTTP server was started, False otherwise.""" 246 if not isinstance(paths, list): 247 paths = [paths] 248 paths = [os.path.abspath(p) for p in paths] 249 250 if paths and self._http_server and self._http_server.paths == paths: 251 return False 252 253 if self._http_server: 254 self._http_server.Close() 255 self._http_server = None 256 257 if not paths: 258 return False 259 260 self._http_server = temporary_http_server.TemporaryHTTPServer( 261 self._browser_backend, paths) 262 263 return True 264 265 def SetReplayArchivePath(self, archive_path, append_to_existing_wpr=False): 266 if self._wpr_server: 267 self._wpr_server.Close() 268 self._wpr_server = None 269 270 if not archive_path: 271 return None 272 273 if self._browser_backend.wpr_mode == wpr_modes.WPR_OFF: 274 return 275 276 use_record_mode = self._browser_backend.wpr_mode == wpr_modes.WPR_RECORD 277 if not use_record_mode: 278 assert os.path.isfile(archive_path) 279 280 self._wpr_server = wpr_server.ReplayServer( 281 self._browser_backend, 282 archive_path, 283 use_record_mode, 284 append_to_existing_wpr, 285 self._browser_backend.WEBPAGEREPLAY_HOST, 286 self._browser_backend.webpagereplay_local_http_port, 287 self._browser_backend.webpagereplay_local_https_port, 288 self._browser_backend.webpagereplay_remote_http_port, 289 self._browser_backend.webpagereplay_remote_https_port) 290 291 def GetStandardOutput(self): 292 return self._browser_backend.GetStandardOutput() 293