Make run-benchmark script supports 'config' key in test plan.
[WebKit-https.git] / Tools / Scripts / webkitpy / benchmark_runner / browser_driver / linux_browser_driver.py
1 # Copyright (C) 2016 Igalia S.L. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 #     * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 import os
28 import sys
29 import tempfile
30 import logging
31 import subprocess
32 try:
33     import psutil
34 except ImportError:
35     pass
36 from webkitpy.benchmark_runner.utils import force_remove
37 from browser_driver import BrowserDriver
38
39
40 _log = logging.getLogger(__name__)
41
42
43 class LinuxBrowserDriver(BrowserDriver):
44     browser_name = None
45     process_search_list = []
46     platform = 'linux'
47
48     def __init__(self):
49         self.process_name = self._get_first_executable_path_from_list(self.process_search_list)
50         if self.process_name is None:
51             raise ValueError('Cant find executable for browser {browser_name}. Searched list: {browser_process_list}'.format(
52                               browser_name=self.browser_name, browser_process_list=self.process_search_list))
53
54     def prepare_env(self, config):
55         self._browser_process = None
56         self._browser_arguments = None
57         self._temp_profiledir = tempfile.mkdtemp()
58         self._test_environ = dict(os.environ)
59         self._test_environ['HOME'] = self._temp_profiledir
60
61     def restore_env(self):
62         force_remove(self._temp_profiledir)
63
64     def close_browsers(self):
65         if self._browser_process:
66             if self._browser_process.poll() is None:  # still running
67                 if 'psutil' in sys.modules:
68                     main_browser_process = psutil.Process(self._browser_process.pid)
69                     browser_children = main_browser_process.children(recursive=True)
70                     _log.info('Killing browser {browser_name} with pid {browser_pid} and cmd: {browser_cmd}'.format(
71                                browser_name=self.browser_name, browser_pid=self._browser_process.pid,
72                                browser_cmd=' '.join(main_browser_process.cmdline()).strip() or main_browser_process.name()))
73                     main_browser_process.kill()
74                     for browser_child in browser_children:
75                         if browser_child.is_running():
76                             _log.info('Killing still alive {browser_name} child with pid {browser_pid} and cmd: {browser_cmd}'.format(
77                                        browser_name=self.browser_name, browser_pid=browser_child.pid,
78                                        browser_cmd=' '.join(browser_child.cmdline()).strip() or browser_child.name()))
79                             browser_child.kill()
80                 else:
81                     _log.info('Killing browser {browser_name} with pid {browser_pid}'.format(
82                                browser_name=self.browser_name, browser_pid=self._browser_process.pid))
83                     self._browser_process.kill()
84                     _log.warning('python psutil not found, cant check for '
85                                  'still-alive browser childs to kill.')
86             else:
87                 _log.error('Browser {browser_name} with pid {browser_pid} ended prematurely with return code {browser_retcode}.'.format(
88                             browser_name=self.browser_name, browser_pid=self._browser_process.pid,
89                             browser_retcode=self._browser_process.returncode))
90
91     def launch_url(self, url, options, browser_build_path):
92         if not self._browser_arguments:
93             self._browser_arguments = [url]
94         exec_args = [self.process_name] + self._browser_arguments
95         _log.info('Executing: {browser_cmdline}'.format(browser_cmdline=' '.join(exec_args)))
96         self._browser_process = subprocess.Popen(exec_args, env=self._test_environ,
97                                                  stdout=subprocess.PIPE,
98                                                  stderr=subprocess.STDOUT)
99
100     def _get_first_executable_path_from_list(self, searchlist):
101         searchpath = [os.path.curdir] + os.environ['PATH'].split(os.pathsep)
102         for program in searchlist:
103             for path in searchpath:
104                 fullpath = os.path.abspath(os.path.join(path, program))
105                 if  os.path.isfile(fullpath) and os.access(fullpath, os.X_OK):
106                     return fullpath
107         return None
108
109     def _screen_size(self):
110         # load_subclasses() from __init__.py will load this file to
111         # check the platform defined. Do here a lazy import instead of
112         # trying to import the Gtk module on the global scope of this
113         # file to avoid ImportError errors on other platforms.
114         # Python imports are cached and only run once, so this should be ok.
115         import gi
116         gi.require_version('Gtk', '3.0')
117         gi.require_version('Gdk', '3.0')
118         from gi.repository import Gtk, Gdk
119         if Gtk.get_major_version() == 3 and Gtk.get_minor_version() < 22:
120             screen = Gtk.Window().get_screen()
121             return screen.get_monitor_geometry(screen.get_primary_monitor())
122         else:
123             display = Gdk.Display.get_default()
124             monitor = display.get_primary_monitor()
125             return monitor.get_geometry()