haikuwebkit/PerformanceTests/LaunchTime/feedback_server.py

98 lines
3.1 KiB
Python
Raw Permalink Normal View History

Add benchmark for WebKit process launch times https://bugs.webkit.org/show_bug.cgi?id=186414 Patch by Ben Richards <benton_richards@apple.com> on 2018-07-19 Reviewed by Ryosuke Niwa. Added two benchmarks, one for measuring browser new tab launch time and one for browser startup time. * LaunchTime/.gitignore: Added. * LaunchTime/feedback_client.html: Added. Displays benchmark progress in browser * LaunchTime/feedback_server.py: Added. (FeedbackServer): Sends data to feedback_client via websocket (FeedbackServer.__init__): (FeedbackServer._create_app): (FeedbackServer._start_server): (FeedbackServer._send_all_messages): (FeedbackServer.start): (FeedbackServer.stop): (FeedbackServer.send_message): Send a message to the feedback_client (FeedbackServer.wait_until_client_has_loaded): Wait until the feedback_client has opened a websocket connection to the feedback_server (FeedbackServer.MainHandler): Handler factory to create handler that serves feedback_client.html (FeedbackServer.MainHandler.Handler): (FeedbackServer.MainHandler.Handler.get): (FeedbackServer.WSHandler): Handler factory to create handler that sends data to feedback client (FeedbackServer.WSHandler.Handler): (FeedbackServer.WSHandler.Handler.open): On websocket connection opened (FeedbackServer.WSHandler.Handler.on_close): On websocket connection closed * LaunchTime/launch_time.py: Added. (DefaultLaunchTimeHandler): Abstract HTTP request handler for launch time benchmarks (DefaultLaunchTimeHandler.get_test_page): Default test page to be overridden by benchmarks (DefaultLaunchTimeHandler.get_blank_page): (DefaultLaunchTimeHandler.on_receive_stop_time): (DefaultLaunchTimeHandler.do_HEAD): (DefaultLaunchTimeHandler.do_GET): (DefaultLaunchTimeHandler.do_POST): (DefaultLaunchTimeHandler.log_message): Suppresses HTTP logs from SimpleHTTPRequestHandler (LaunchTimeBenchmark): Abstract class which launch time benchmarks inherit from and override methods desired to customize (LaunchTimeBenchmark.__init__): (LaunchTimeBenchmark._parse_browser_bundle_path): Parser for bundle path option (LaunchTimeBenchmark._parse_args): (LaunchTimeBenchmark._run_server): Target function for main server thread (LaunchTimeBenchmark._setup_servers): (LaunchTimeBenchmark._clean_up): (LaunchTimeBenchmark._exit_due_to_exception): (LaunchTimeBenchmark._geometric_mean): (LaunchTimeBenchmark._standard_deviation): (LaunchTimeBenchmark._compute_results): Returns mean and std dev of list of results and pretty prints if should_print=True is specified (LaunchTimeBenchmark._wait_times): Mimic numpy.linspace (LaunchTimeBenchmark.open_tab): Open a browser tab with the html given by self.response_handler.get_test_page (LaunchTimeBenchmark.launch_browser): Open a broser to either the feedback client (if option is set) or a blank page (LaunchTimeBenchmark.quit_browser): (LaunchTimeBenchmark.quit_browser.quit_app): (LaunchTimeBenchmark.quit_browser.is_app_closed): (LaunchTimeBenchmark.close_tab): (LaunchTimeBenchmark.wait): (LaunchTimeBenchmark.log): Print to console and send to feedback client if --feedback-in-browser flag is used (LaunchTimeBenchmark.log_verbose): Only logs if --verbose flag is used (LaunchTimeBenchmark.run): (LaunchTimeBenchmark.group_init): Initialization done before each round of iterations (LaunchTimeBenchmark.run_iteration): (LaunchTimeBenchmark.initialize): Convenience method to be overriden by subclasses which is called at the end of __init__ (LaunchTimeBenchmark.will_parse_arguments): Called before argparse.parse_args to let subclasses add new command line arguments (LaunchTimeBenchmark.did_parse_arguments): Called after argparse.parse_args to let subclass initialize based on command line arguments * LaunchTime/new_tab.py: Added (NewTabBenchmark): (NewTabBenchmark._parse_wait_time): Parser for wait time option (NewTabBenchmark.initialize): (NewTabBenchmark.run_iteration): (NewTabBenchmark.group_init): (NewTabBenchmark.will_parse_arguments): (NewTabBenchmark.did_parse_arguments): (NewTabBenchmark.ResponseHandler): (NewTabBenchmark.ResponseHandler.Handler): (NewTabBenchmark.ResponseHandler.Handler.get_test_page): * LaunchTime/startup.py: Added (StartupBenchmark): This benchmark measures browser startup time and initial page load time (StartupBenchmark.initialize): (StartupBenchmark.run_iteration): (StartupBenchmark.ResponseHandler): (StartupBenchmark.ResponseHandler.Handler): (StartupBenchmark.ResponseHandler.Handler.get_test_page): * LaunchTime/thirdparty/__init__.py: Added. (AutoinstallImportHook): Auto installs tornado package for feedback server (AutoinstallImportHook.__init__): (AutoinstallImportHook._ensure_autoinstalled_dir_is_in_sys_path): (AutoinstallImportHook.find_module): (AutoinstallImportHook._install_tornado): (AutoinstallImportHook.greater_than_equal_to_version): (AutoinstallImportHook._install): (AutoinstallImportHook.get_latest_pypi_url): (AutoinstallImportHook.install_binary): (autoinstall_everything): (get_os_info): Canonical link: https://commits.webkit.org/203012@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@234006 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2018-07-19 21:54:48 +00:00
from Queue import Queue
import logging
import os
import socket
import threading
import time
# This line makes sure that tornado is installed and in sys.path
import thirdparty.autoinstalled.tornado
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.template
from tornado.httpserver import HTTPServer
class FeedbackServer:
def __init__(self):
self._client = None
self._feedback_server_thread = None
self._port = 9090
self._application = None
self._server_is_ready = None
self._io_loop = None
self._messages = Queue()
self._client_loaded = threading.Semaphore(0)
def _create_app(self):
return HTTPServer(tornado.web.Application([
(r'/ws', FeedbackServer.WSHandler(self)),
(r'/', FeedbackServer.MainHandler(self)),
]))
def _start_server(self):
self._io_loop = tornado.ioloop.IOLoop()
self._io_loop.make_current()
self._application = self._create_app()
while True:
try:
self._application.listen(self._port)
print 'Running feedback server at http://localhost:{}'.format(self._port)
break
except socket.error as err:
self._port += 1
except:
print 'Feedback server failed to start'
break
self._server_is_ready.release()
self._io_loop.start()
def _send_all_messages(self):
if self._client:
while not self._messages.empty():
message = self._messages.get()
self._client.write_message(message)
def start(self):
self._server_is_ready = threading.Semaphore(0)
self._feedback_server_thread = threading.Thread(target=self._start_server)
self._feedback_server_thread.start()
self._server_is_ready.acquire()
return self._port
def stop(self):
self._client_loaded = threading.Semaphore(0)
self._application.stop()
self._io_loop.add_callback(lambda x: x.stop(), self._io_loop)
self._feedback_server_thread.join()
def send_message(self, new_message):
self._messages.put(new_message)
self._io_loop.add_callback(self._send_all_messages)
def wait_until_client_has_loaded(self):
self._client_loaded.acquire()
@staticmethod
def MainHandler(feedback_server):
class Handler(tornado.web.RequestHandler):
def get(self):
loader = tornado.template.Loader('.')
client_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'feedback_client.html')
self.write(loader.load(client_path).generate(port=feedback_server._port))
return Handler
@staticmethod
def WSHandler(feedback_server):
class Handler(tornado.websocket.WebSocketHandler):
def open(self):
feedback_server._client = self
feedback_server._client_loaded.release()
def on_close(self):
feedback_server._client = None
return Handler