1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
#!/usr/bin/env python
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utilities for Cronet performance tests."""
import logging
import os
import posixpath
import subprocess
import tempfile
from time import sleep
from cronet.tools import android_rndis_forwarder
# pylint: disable=useless-object-inheritance
REPOSITORY_ROOT = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', '..', '..'))
BUILD_TYPE = 'Release'
BUILD_DIR = os.path.join(REPOSITORY_ROOT, 'out', BUILD_TYPE)
QUIC_SERVER = os.path.join(BUILD_DIR, 'quic_server')
CERT_PATH = os.path.join('net', 'data', 'ssl', 'certificates')
QUIC_CERT_DIR = os.path.join(REPOSITORY_ROOT, CERT_PATH)
QUIC_CERT_HOST = 'test.example.com'
QUIC_CERT_FILENAME = 'quic-chain.pem'
QUIC_CERT = os.path.join(QUIC_CERT_DIR, QUIC_CERT_FILENAME)
QUIC_KEY = os.path.join(QUIC_CERT_DIR, 'quic-leaf-cert.key')
APP_APK = os.path.join(BUILD_DIR, 'apks', 'CronetPerfTest.apk')
APP_PACKAGE = 'org.chromium.net'
APP_ACTIVITY = '.CronetPerfTestActivity'
APP_ACTION = 'android.intent.action.MAIN'
HTTP_PORT = None # Value will be overridden by DEFAULT_BENCHMARK_CONFIG.
# TODO(pauljensen): Consider whether we can avoid loading this
# DEFAULT_BENCHMARK_CONFIG dict into globals.
DEFAULT_BENCHMARK_CONFIG = {
# Control various metric recording for further investigation.
'CAPTURE_NETLOG': False,
'CAPTURE_TRACE': False,
'CAPTURE_SAMPLED_TRACE': False,
# While running Cronet Async API benchmarks, indicate if callbacks should be
# run on network thread rather than posted back to caller thread. This allows
# measuring if thread-hopping overhead is significant.
'CRONET_ASYNC_USE_NETWORK_THREAD': False,
# A small resource for device to fetch from host.
'SMALL_RESOURCE': 'small.html',
'SMALL_RESOURCE_SIZE': 26,
# Number of times to fetch SMALL_RESOURCE.
'SMALL_ITERATIONS': 1000,
# A large resource for device to fetch from host.
'LARGE_RESOURCE': 'large.html',
'LARGE_RESOURCE_SIZE': 10000026,
# Number of times to fetch LARGE_RESOURCE.
'LARGE_ITERATIONS': 4,
# Ports of HTTP and QUIC servers on host.
'HTTP_PORT': 9000,
'QUIC_PORT': 9001,
# Maximum read/write buffer size to use.
'MAX_BUFFER_SIZE': 16384,
'HOST': QUIC_CERT_HOST,
'QUIC_CERT_FILE': QUIC_CERT_FILENAME,
}
# Add benchmark config to global state for easy access.
globals().update(DEFAULT_BENCHMARK_CONFIG)
# Pylint doesn't really interpret the file, so it won't find the definitions
# added from DEFAULT_BENCHMARK_CONFIG, so suppress the undefined variable and
# bad string format type warnings.
#pylint: disable=undefined-variable,bad-string-format-type
class NativeDevice(object):
def GetExternalStoragePath(self):
return '/tmp'
def RunShellCommand(self, cmd, check_return=False):
if check_return:
subprocess.check_call(cmd)
else:
subprocess.call(cmd)
def WriteFile(self, path, data):
with open(path, 'w') as f:
f.write(data)
def GetConfig(device):
config = DEFAULT_BENCHMARK_CONFIG
config['HOST_IP'] = GetServersHost(device)
if isinstance(device, NativeDevice):
config['RESULTS_FILE'] = '/tmp/cronet_perf_test_results.txt'
config['DONE_FILE'] = '/tmp/cronet_perf_test_done.txt'
else:
# An on-device file containing benchmark timings. Written by benchmark app.
config['RESULTS_FILE'] = '/data/data/' + APP_PACKAGE + '/results.txt'
# An on-device file whose presence indicates benchmark app has terminated.
config['DONE_FILE'] = '/data/data/' + APP_PACKAGE + '/done.txt'
return config
def GetAndroidRndisConfig(device):
return android_rndis_forwarder.AndroidRndisConfigurator(device)
def GetServersHost(device):
if isinstance(device, NativeDevice):
return '127.0.0.1'
return GetAndroidRndisConfig(device).host_ip
def GetHttpServerURL(device, resource):
return 'http://%s:%d/%s' % (GetServersHost(device), HTTP_PORT, resource)
class QuicServer(object):
def __init__(self, quic_server_doc_root):
self._process = None
self._quic_server_doc_root = quic_server_doc_root
def StartupQuicServer(self, device):
cmd = [QUIC_SERVER,
'--quic_response_cache_dir=%s' % self._quic_server_doc_root,
'--certificate_file=%s' % QUIC_CERT,
'--key_file=%s' % QUIC_KEY,
'--port=%d' % QUIC_PORT]
logging.info("Starting Quic Server: %s", cmd)
self._process = subprocess.Popen(cmd)
assert self._process is not None
# Wait for quic_server to start serving.
waited_s = 0
while subprocess.call(['lsof', '-i', 'udp:%d' % QUIC_PORT, '-p',
'%d' % self._process.pid],
stdout=open(os.devnull, 'w')) != 0:
sleep(0.1)
waited_s += 0.1
assert waited_s < 5, "quic_server failed to start after %fs" % waited_s
# Push certificate to device.
cert = open(QUIC_CERT, 'r').read()
device_cert_path = posixpath.join(
device.GetExternalStoragePath(), 'chromium_tests_root', CERT_PATH)
device.RunShellCommand(['mkdir', '-p', device_cert_path], check_return=True)
device.WriteFile(os.path.join(device_cert_path, QUIC_CERT_FILENAME), cert)
def ShutdownQuicServer(self):
if self._process:
self._process.terminate()
def GenerateHttpTestResources():
http_server_doc_root = tempfile.mkdtemp()
# Create a small test file to serve.
small_file_name = os.path.join(http_server_doc_root, SMALL_RESOURCE)
small_file = open(small_file_name, 'wb')
small_file.write('<html><body></body></html>');
small_file.close()
assert SMALL_RESOURCE_SIZE == os.path.getsize(small_file_name)
# Create a large (10MB) test file to serve.
large_file_name = os.path.join(http_server_doc_root, LARGE_RESOURCE)
large_file = open(large_file_name, 'wb')
large_file.write('<html><body>');
for _ in range(0, 1000000):
large_file.write('1234567890');
large_file.write('</body></html>');
large_file.close()
assert LARGE_RESOURCE_SIZE == os.path.getsize(large_file_name)
return http_server_doc_root
def GenerateQuicTestResources(device):
quic_server_doc_root = tempfile.mkdtemp()
# Use wget to build up fake QUIC in-memory cache dir for serving.
# quic_server expects the dir/file layout that wget produces.
for resource in [SMALL_RESOURCE, LARGE_RESOURCE]:
assert subprocess.Popen(['wget', '-p', '-q', '--save-headers',
GetHttpServerURL(device, resource)],
cwd=quic_server_doc_root).wait() == 0
# wget places results in host:port directory. Adjust for QUIC port.
os.rename(os.path.join(quic_server_doc_root,
"%s:%d" % (GetServersHost(device), HTTP_PORT)),
os.path.join(quic_server_doc_root,
"%s:%d" % (QUIC_CERT_HOST, QUIC_PORT)))
return quic_server_doc_root
def GenerateLighttpdConfig(config_file, http_server_doc_root, http_server):
# Must create customized config file to allow overriding the server.bind
# setting.
config_file.write('server.document-root = "%s"\n' % http_server_doc_root)
config_file.write('server.port = %d\n' % HTTP_PORT)
# These lines are added so lighttpd_server.py's internal test succeeds.
config_file.write('server.tag = "%s"\n' % http_server.server_tag)
config_file.write('server.pid-file = "%s"\n' % http_server.pid_file)
config_file.write('dir-listing.activate = "enable"\n')
config_file.flush()
|