Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / browser.py
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
5 import os
6
7 from telemetry.core import browser_credentials
8 from telemetry.core import exceptions
9 from telemetry.core import extension_dict
10 from telemetry.core import platform
11 from telemetry.core import tab_list
12 from telemetry.core import temporary_http_server
13 from telemetry.core import wpr_modes
14 from telemetry.core import wpr_server
15 from telemetry.core.backends import browser_backend
16 from telemetry.core.platform.profiler import profiler_finder
17
18
19 class Browser(object):
20   """A running browser instance that can be controlled in a limited way.
21
22   To create a browser instance, use browser_finder.FindBrowser.
23
24   Be sure to clean up after yourself by calling Close() when you are done with
25   the browser. Or better yet:
26     browser_to_create = FindBrowser(options)
27     with browser_to_create.Create() as browser:
28       ... do all your operations on browser here
29   """
30   def __init__(self, backend, platform_backend):
31     self._browser_backend = backend
32     self._http_server = None
33     self._wpr_server = None
34     self._platform = platform.Platform(platform_backend)
35     self._platform_backend = platform_backend
36     self._tabs = tab_list.TabList(backend.tab_list_backend)
37     self._extensions = None
38     if backend.supports_extensions:
39       self._extensions = extension_dict.ExtensionDict(
40           backend.extension_dict_backend)
41     self.credentials = browser_credentials.BrowserCredentials()
42     self._platform.SetFullPerformanceModeEnabled(True)
43     self._active_profilers = []
44     self._profilers_states = {}
45
46   def __enter__(self):
47     self.Start()
48     return self
49
50   def __exit__(self, *args):
51     self.Close()
52
53   @property
54   def platform(self):
55     return self._platform
56
57   @property
58   def browser_type(self):
59     return self._browser_backend.browser_type
60
61   @property
62   def is_content_shell(self):
63     """Returns whether this browser is a content shell, only."""
64     return self._browser_backend.is_content_shell
65
66   @property
67   def supports_extensions(self):
68     return self._browser_backend.supports_extensions
69
70   @property
71   def supports_tab_control(self):
72     return self._browser_backend.supports_tab_control
73
74   @property
75   def tabs(self):
76     return self._tabs
77
78   @property
79   def extensions(self):
80     """Returns the extension dictionary if it exists."""
81     if not self.supports_extensions:
82       raise browser_backend.ExtensionsNotSupportedException(
83           'Extensions not supported')
84     return self._extensions
85
86   @property
87   def supports_tracing(self):
88     return self._browser_backend.supports_tracing
89
90   def is_profiler_active(self, profiler_name):
91     return profiler_name in [profiler.name() for
92                              profiler in self._active_profilers]
93
94   def _GetStatsCommon(self, pid_stats_function):
95     browser_pid = self._browser_backend.pid
96     result = {
97         'Browser': dict(pid_stats_function(browser_pid), **{'ProcessCount': 1}),
98         'Renderer': {'ProcessCount': 0},
99         'Gpu': {'ProcessCount': 0},
100         'Other': {'ProcessCount': 0}
101     }
102     process_count = 1
103     for child_pid in self._platform_backend.GetChildPids(browser_pid):
104       try:
105         child_cmd_line = self._platform_backend.GetCommandLine(child_pid)
106         child_stats = pid_stats_function(child_pid)
107       except exceptions.ProcessGoneException:
108         # It is perfectly fine for a process to have gone away between calling
109         # GetChildPids() and then further examining it.
110         continue
111       child_process_name = self._browser_backend.GetProcessName(child_cmd_line)
112       process_name_type_key_map = {'gpu-process': 'Gpu', 'renderer': 'Renderer'}
113       if child_process_name in process_name_type_key_map:
114         child_process_type_key = process_name_type_key_map[child_process_name]
115       else:
116         # TODO: identify other process types (zygote, plugin, etc), instead of
117         # lumping them in a single category.
118         child_process_type_key = 'Other'
119       result[child_process_type_key]['ProcessCount'] += 1
120       for k, v in child_stats.iteritems():
121         if k in result[child_process_type_key]:
122           result[child_process_type_key][k] += v
123         else:
124           result[child_process_type_key][k] = v
125       process_count += 1
126     for v in result.itervalues():
127       if v['ProcessCount'] > 1:
128         for k in v.keys():
129           if k.endswith('Peak'):
130             del v[k]
131       del v['ProcessCount']
132     result['ProcessCount'] = process_count
133     return result
134
135   @property
136   def memory_stats(self):
137     """Returns a dict of memory statistics for the browser:
138     { 'Browser': {
139         'VM': S,
140         'VMPeak': T,
141         'WorkingSetSize': U,
142         'WorkingSetSizePeak': V,
143         'ProportionalSetSize': W,
144         'PrivateDirty': X
145       },
146       'Gpu': {
147         'VM': S,
148         'VMPeak': T,
149         'WorkingSetSize': U,
150         'WorkingSetSizePeak': V,
151         'ProportionalSetSize': W,
152         'PrivateDirty': X
153       },
154       'Renderer': {
155         'VM': S,
156         'VMPeak': T,
157         'WorkingSetSize': U,
158         'WorkingSetSizePeak': V,
159         'ProportionalSetSize': W,
160         'PrivateDirty': X
161       },
162       'SystemCommitCharge': Y,
163       'ProcessCount': Z,
164     }
165     Any of the above keys may be missing on a per-platform basis.
166     """
167     self._platform_backend.PurgeUnpinnedMemory()
168     result = self._GetStatsCommon(self._platform_backend.GetMemoryStats)
169     result['SystemCommitCharge'] = \
170         self._platform_backend.GetSystemCommitCharge()
171     return result
172
173   @property
174   def cpu_stats(self):
175     """Returns a dict of cpu statistics for the system.
176     { 'Browser': {
177         'CpuProcessTime': S,
178         'TotalTime': T
179       },
180       'Gpu': {
181         'CpuProcessTime': S,
182         'TotalTime': T
183       },
184       'Renderer': {
185         'CpuProcessTime': S,
186         'TotalTime': T
187       }
188     }
189     Any of the above keys may be missing on a per-platform basis.
190     """
191     result = self._GetStatsCommon(self._platform_backend.GetCpuStats)
192     del result['ProcessCount']
193
194     # We want a single time value, not the sum for all processes.
195     for process_type in result:
196       # Skip any process_types that are empty
197       if not len(result[process_type]):
198         continue
199       result[process_type].update(self._platform_backend.GetCpuTimestamp())
200     return result
201
202   @property
203   def io_stats(self):
204     """Returns a dict of IO statistics for the browser:
205     { 'Browser': {
206         'ReadOperationCount': W,
207         'WriteOperationCount': X,
208         'ReadTransferCount': Y,
209         'WriteTransferCount': Z
210       },
211       'Gpu': {
212         'ReadOperationCount': W,
213         'WriteOperationCount': X,
214         'ReadTransferCount': Y,
215         'WriteTransferCount': Z
216       },
217       'Renderer': {
218         'ReadOperationCount': W,
219         'WriteOperationCount': X,
220         'ReadTransferCount': Y,
221         'WriteTransferCount': Z
222       }
223     }
224     """
225     result = self._GetStatsCommon(self._platform_backend.GetIOStats)
226     del result['ProcessCount']
227     return result
228
229   def StartProfiling(self, profiler_name, base_output_file):
230     """Starts profiling using |profiler_name|. Results are saved to
231     |base_output_file|.<process_name>."""
232     assert not self._active_profilers, 'Already profiling. Must stop first.'
233
234     profiler_class = profiler_finder.FindProfiler(profiler_name)
235
236     if not profiler_class.is_supported(self._browser_backend.browser_type):
237       raise Exception('The %s profiler is not '
238                       'supported on this platform.' % profiler_name)
239
240     if not profiler_class in self._profilers_states:
241       self._profilers_states[profiler_class] = {}
242
243     self._active_profilers.append(
244         profiler_class(self._browser_backend, self._platform_backend,
245             base_output_file, self._profilers_states[profiler_class]))
246
247   def StopProfiling(self):
248     """Stops all active profilers and saves their results.
249
250     Returns:
251       A list of filenames produced by the profiler.
252     """
253     output_files = []
254     for profiler in self._active_profilers:
255       output_files.extend(profiler.CollectProfile())
256     self._active_profilers = []
257     return output_files
258
259   def StartTracing(self, custom_categories=None, timeout=10):
260     return self._browser_backend.StartTracing(custom_categories, timeout)
261
262   def StopTracing(self):
263     return self._browser_backend.StopTracing()
264
265   def Start(self):
266     browser_options = self._browser_backend.browser_options
267     if browser_options.clear_sytem_cache_for_browser_and_profile_on_start:
268       if self._platform.CanFlushIndividualFilesFromSystemCache():
269         self._platform.FlushSystemCacheForDirectory(
270             self._browser_backend.profile_directory)
271         self._platform.FlushSystemCacheForDirectory(
272             self._browser_backend.browser_directory)
273       else:
274         self._platform.FlushEntireSystemCache()
275
276     self._browser_backend.Start()
277     self._browser_backend.SetBrowser(self)
278
279   def Close(self):
280     """Closes this browser."""
281     for profiler_class in self._profilers_states:
282       profiler_class.WillCloseBrowser(self._browser_backend,
283                                       self._platform_backend)
284
285     self._platform.SetFullPerformanceModeEnabled(False)
286
287     if self._wpr_server:
288       self._wpr_server.Close()
289       self._wpr_server = None
290
291     if self._http_server:
292       self._http_server.Close()
293       self._http_server = None
294
295     self._browser_backend.Close()
296     self.credentials = None
297
298   @property
299   def http_server(self):
300     return self._http_server
301
302   def SetHTTPServerDirectories(self, paths):
303     """Returns True if the HTTP server was started, False otherwise."""
304     if isinstance(paths, basestring):
305       paths = set([paths])
306     paths = set(os.path.realpath(p) for p in paths)
307
308     # If any path is in a subdirectory of another, remove the subdirectory.
309     duplicates = set()
310     for parent_path in paths:
311       for sub_path in paths:
312         if parent_path == sub_path:
313           continue
314         if os.path.commonprefix((parent_path, sub_path)) == parent_path:
315           duplicates.add(sub_path)
316     paths -= duplicates
317
318     if self._http_server:
319       if paths and self._http_server.paths == paths:
320         return False
321
322       self._http_server.Close()
323       self._http_server = None
324
325     if not paths:
326       return False
327
328     self._http_server = temporary_http_server.TemporaryHTTPServer(
329       self._browser_backend, paths)
330
331     return True
332
333   def SetReplayArchivePath(self, archive_path, append_to_existing_wpr=False,
334                            make_javascript_deterministic=True):
335     if self._wpr_server:
336       self._wpr_server.Close()
337       self._wpr_server = None
338
339     if not archive_path:
340       return None
341
342     if self._browser_backend.wpr_mode == wpr_modes.WPR_OFF:
343       return
344
345     use_record_mode = self._browser_backend.wpr_mode == wpr_modes.WPR_RECORD
346     if not use_record_mode:
347       assert os.path.isfile(archive_path)
348
349     self._wpr_server = wpr_server.ReplayServer(
350         self._browser_backend,
351         archive_path,
352         use_record_mode,
353         append_to_existing_wpr,
354         make_javascript_deterministic)
355
356   def GetStandardOutput(self):
357     return self._browser_backend.GetStandardOutput()
358
359   def GetStackTrace(self):
360     return self._browser_backend.GetStackTrace()
361
362   @property
363   def supports_system_info(self):
364     return self._browser_backend.supports_system_info
365
366   def GetSystemInfo(self):
367     """Returns low-level information about the system, if available.
368
369        See the documentation of the SystemInfo class for more details."""
370     return self._browser_backend.GetSystemInfo()