Commit 608d351f authored by Paul Jensen's avatar Paul Jensen Committed by Commit Bot

[Cronet] Native performance test

Bug: 879211
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;master.tryserver.chromium.android:android_cronet_tester
Change-Id: Id3f234c20fb0bf28fab51a4697d8fab7b473ba73
Reviewed-on: https://chromium-review.googlesource.com/c/1207196
Commit-Queue: Paul Jensen <pauljensen@chromium.org>
Reviewed-by: default avatarMisha Efimov <mef@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597119}
parent ab036811
......@@ -242,4 +242,20 @@ if (!is_ios && !is_android) {
":generate_license",
]
}
executable("cronet_native_perf_test") {
testonly = true
sources = [
"native/perftest/main.cc",
"native/perftest/perf_test.cc",
]
deps = [
"//base",
"//components/cronet",
"//components/cronet/native:cronet_native_headers",
"//components/cronet/native/test:cronet_native_tests",
"//components/cronet/native/test:cronet_native_testutil",
"//net:test_support",
]
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/cronet/native/perftest/perf_test.h"
#include "base/logging.h"
// When invoked, passes first and only argument to native performance test.
int main(int argc, char* argv[]) {
CHECK_EQ(argc, 2) << "Must include experimental options in JSON as only arg.";
PerfTest(argv[1]);
return 0;
}
\ No newline at end of file
This diff is collapsed.
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Run Cronet native performance test. |json_args| is the string containing
// the JSON formatted arguments from components/cronet/native/perftest/run.py.
void PerfTest(const char* json_args);
#!/usr/bin/env python
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This script runs an automated Cronet native performance benchmark.
This script:
1. Starts HTTP and QUIC servers on the host machine.
2. Runs benchmark executable.
Prerequisites:
1. quic_server and cronet_native_perf_test have been built for the host machine,
e.g. via:
gn gen out/Release --args="is_debug=false"
ninja -C out/Release quic_server cronet_native_perf_test
2. sudo apt-get install lighttpd
Invocation:
./run.py
Output:
Benchmark timings are output to /tmp/cronet_perf_test_results.txt
"""
import json
import os
import shutil
import sys
import tempfile
REPOSITORY_ROOT = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', '..', '..', '..'))
sys.path.append(os.path.join(REPOSITORY_ROOT, 'build', 'android'))
import lighttpd_server
sys.path.append(os.path.join(REPOSITORY_ROOT, 'components', 'cronet', 'tools'))
import perf_test_utils
def main():
device = perf_test_utils.NativeDevice()
# Start HTTP server.
http_server_doc_root = perf_test_utils.GenerateHttpTestResources()
config_file = tempfile.NamedTemporaryFile()
http_server = lighttpd_server.LighttpdServer(http_server_doc_root,
port=perf_test_utils.HTTP_PORT, base_config_path=config_file.name)
perf_test_utils.GenerateLighttpdConfig(config_file, http_server_doc_root,
http_server)
assert http_server.StartupHttpServer()
config_file.close()
# Start QUIC server.
quic_server_doc_root = perf_test_utils.GenerateQuicTestResources(device)
quic_server = perf_test_utils.QuicServer(quic_server_doc_root)
quic_server.StartupQuicServer(device)
# Run test
os.environ['LD_LIBRARY_PATH'] = perf_test_utils.BUILD_DIR
device.RunShellCommand(
[os.path.join(perf_test_utils.BUILD_DIR, 'cronet_native_perf_test'),
json.dumps(perf_test_utils.GetConfig(device))],
check_return=True)
# Shutdown.
quic_server.ShutdownQuicServer()
shutil.rmtree(quic_server_doc_root)
http_server.ShutdownHttpServer()
shutil.rmtree(http_server_doc_root)
if __name__ == '__main__':
main()
......@@ -40,7 +40,7 @@ class TestUploadDataProvider {
TestUploadDataProvider(SuccessCallbackMode success_callback_mode,
Cronet_ExecutorPtr executor);
~TestUploadDataProvider();
virtual ~TestUploadDataProvider();
Cronet_UploadDataProviderPtr CreateUploadDataProvider();
......@@ -64,11 +64,12 @@ class TestUploadDataProvider {
/**
* Returns the cumulative length of all data added by calls to addRead.
*/
int64_t GetLength() const;
virtual int64_t GetLength() const;
int64_t GetUploadedLength() const;
void Read(Cronet_UploadDataSinkPtr upload_data_sink, Cronet_BufferPtr buffer);
virtual void Read(Cronet_UploadDataSinkPtr upload_data_sink,
Cronet_BufferPtr buffer);
void Rewind(Cronet_UploadDataSinkPtr upload_data_sink);
......
......@@ -110,7 +110,7 @@ void Cronet_UploadDataSinkImpl::OnReadSucceeded(uint64_t bytes_read,
}
CHECK(bytes_read > 0 || (final_chunk && bytes_read == 0));
// Bytes read exceeds buffer length.
CHECK_LT(static_cast<size_t>(bytes_read), buffer_->io_buffer_len());
CHECK_LE(static_cast<size_t>(bytes_read), buffer_->io_buffer_len());
if (!is_chunked_) {
// Only chunked upload can have the final chunk.
CHECK(!final_chunk);
......
......@@ -9,6 +9,13 @@ import re
import socket
import struct
import subprocess
import sys
REPOSITORY_ROOT = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', '..', '..'))
sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools', 'perf'))
from core import path_util
sys.path.append(path_util.GetTelemetryDir())
from telemetry.core import platform
from telemetry.core import util
......@@ -282,7 +289,11 @@ function doit() {
# For some combinations of devices and host kernels, adb won't work unless the
# interface is up, but if we bring it up immediately, it will break adb.
#sleep 1
ifconfig rndis0 %(device_ip_address)s netmask 255.255.255.0 up
if ip link show rndis0 ; then
ifconfig rndis0 %(device_ip_address)s netmask 255.255.255.0 up
else
ifconfig usb0 %(device_ip_address)s netmask 255.255.255.0 up
fi
echo DONE >> %(prefix)s.log
}
......
#!/usr/bin/env python
# Copyright 2018 The Chromium Authors. All rights reserved.
# 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 android_rndis_forwarder
import logging
import os
import posixpath
import subprocess
import tempfile
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'
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
# warning.
#pylint: disable=undefined-variable
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 != 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()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment