Add a new script to upload perf tests.
Also add a script to do the bridge between a python 2 and a python 3 interpreter.
This should be removed when the merge scripts will be using python 3 (https://crbug.com/webrtc/13835).
Note that webrtc_dashboard_upload.py will be removed when the new script is stabilized.
Bug: webrtc:13806
Change-Id: I806fa11f417ef37674bdaeb5126c71570e3697d7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/255560
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Christoffer Jansson <jansson@google.com>
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Christoffer Jansson <jansson@webrtc.org>
Commit-Queue: Jeremy Leconte <jleconte@google.com>
Cr-Commit-Position: refs/heads/main@{#36252}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 5c7db2e..f01338f 100755
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -820,9 +820,10 @@
return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
excluded_files = [
- # This test should be run manually after webrtc_dashboard_upload target
+ # These tests should be run manually after webrtc_dashboard_upload target
# has been built.
- 'catapult_uploader_test.py'
+ 'catapult_uploader_test.py',
+ 'process_perf_results_test.py',
]
test_directories = [
diff --git a/tools_webrtc/perf/catapult_uploader.py b/tools_webrtc/perf/catapult_uploader.py
index c33ac89..d07c287 100644
--- a/tools_webrtc/perf/catapult_uploader.py
+++ b/tools_webrtc/perf/catapult_uploader.py
@@ -14,6 +14,7 @@
import time
import zlib
+from typing import Optional
import dataclasses
import httplib2
@@ -53,7 +54,7 @@
build_page_url: str
dashboard_url: str
input_results_file: str
- output_json_file: str
+ output_json_file: Optional[str] = None
wait_timeout_sec: datetime.timedelta = datetime.timedelta(seconds=1200)
wait_polling_period_sec: datetime.timedelta = datetime.timedelta(seconds=120)
@@ -305,5 +306,5 @@
exit_code = UploadToDashboardImpl(options)
except RuntimeError as e:
print(e)
- return 2
+ return 1
return exit_code
diff --git a/tools_webrtc/perf/process_perf_results.py b/tools_webrtc/perf/process_perf_results.py
new file mode 100644
index 0000000..e91b1f6
--- /dev/null
+++ b/tools_webrtc/perf/process_perf_results.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+"""Adds build info to perf results and uploads them.
+
+The tests don't know which bot executed the tests or at what revision, so we
+need to take their output and enrich it with this information. We load the proto
+from the tests, add the build information as shared diagnostics and then
+upload it to the dashboard.
+
+This script can't be in recipes, because we can't access the catapult APIs from
+there. It needs to be here source-side.
+"""
+
+import argparse
+import json
+import os
+import sys
+
+from pathlib import Path
+
+# Even if protobuf is not used directly, this allows transitive imports
+# of the protobuf library to use the vpython wheel specified in the root
+# level .vpython (see bugs.webrtc.org/12211 for context).
+import google.protobuf # pylint: disable=unused-import
+
+
+def _ConfigurePythonPath(outdir):
+ # We just yank the python scripts we require into the PYTHONPATH. You could
+ # also imagine a solution where we use for instance
+ # protobuf:py_proto_runtime to copy catapult and protobuf code to out/.
+ # This is the convention in Chromium and WebRTC python scripts. We do need
+ # to build histogram_pb2 however, so that's why we add out/ to sys.path
+ # below.
+ #
+ # It would be better if there was an equivalent to py_binary in GN, but
+ # there's not.
+ script_dir = os.path.dirname(os.path.realpath(__file__))
+ checkout_root = os.path.abspath(os.path.join(script_dir, os.pardir,
+ os.pardir))
+
+ sys.path.insert(
+ 0, os.path.join(checkout_root, 'third_party', 'catapult', 'tracing'))
+ sys.path.insert(
+ 0, os.path.join(checkout_root, 'third_party', 'protobuf', 'python'))
+
+ # The webrtc_dashboard_upload gn rule will build the protobuf stub for
+ # python, so put it in the path for this script before we attempt to import
+ # it.
+ histogram_proto_path = os.path.join(outdir, 'pyproto', 'tracing', 'tracing',
+ 'proto')
+ sys.path.insert(0, histogram_proto_path)
+
+ # Fail early in case the proto hasn't been built.
+ from tracing.proto import histogram_proto
+ if not histogram_proto.HAS_PROTO:
+ print('Could not find histogram_pb2. You need to build the '
+ 'webrtc_dashboard_upload target before invoking this '
+ 'script. Expected to find '
+ 'histogram_pb2.py in %s.' % histogram_proto_path)
+ return 1
+ return 0
+
+
+def _UploadToDasboard(args):
+ build_properties = json.loads(args.build_properties)
+ exit_code = _ConfigurePythonPath(build_properties['outdir'])
+ if exit_code != 0:
+ return exit_code
+
+ import catapult_uploader
+
+ perftest_outputs = [
+ f.absolute() for f in Path(args.task_output_dir).rglob('perftest-output*')
+ if f.is_file()
+ ]
+ for perftest_output in perftest_outputs:
+ uploader_options = catapult_uploader.UploaderOptions(
+ perf_dashboard_machine_group=(
+ build_properties['perf_dashboard_machine_group']),
+ bot=build_properties['bot'],
+ webrtc_git_hash=build_properties['webrtc_git_hash'],
+ commit_position=build_properties['commit_position'],
+ build_page_url=build_properties['build_page_url'],
+ dashboard_url=build_properties['dashboard_url'],
+ test_suite=args.test_suite,
+ input_results_file=perftest_output,
+ )
+ exit_code = catapult_uploader.UploadToDashboard(uploader_options)
+ if exit_code != 0:
+ return exit_code
+ return 0
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--build-properties', help=argparse.SUPPRESS)
+ parser.add_argument('--summary-json', help=argparse.SUPPRESS)
+ parser.add_argument('--task-output-dir', help=argparse.SUPPRESS)
+ parser.add_argument('--test-suite', help=argparse.SUPPRESS)
+ parser.add_argument('-o', '--output-json', help=argparse.SUPPRESS)
+ parser.add_argument('json_files', nargs='*', help=argparse.SUPPRESS)
+ args = parser.parse_args()
+
+ exit_code = _UploadToDasboard(args)
+ if exit_code != 0:
+ with open(args.output_json, 'w') as f:
+ json.dump({
+ "global_tags": ["UNRELIABLE_RESULTS"],
+ "missing_shards": [0]
+ }, f)
+ return exit_code
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools_webrtc/perf/process_perf_results_py2.py b/tools_webrtc/perf/process_perf_results_py2.py
new file mode 100644
index 0000000..14b6858
--- /dev/null
+++ b/tools_webrtc/perf/process_perf_results_py2.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+"""Calls process_perf_results.py with a python 3 interpreter."""
+
+import sys
+import subprocess
+
+
+# TODO(crbug.com/webrtc/13835): Delete this file and use
+# process_perf_results.py instead.
+def main():
+ cmd = sys.argv[0].replace('_py2', '')
+ print('Calling "%s" with py3 in case this script was called with py2.' % cmd)
+ return subprocess.call(['vpython3', cmd] + sys.argv[1:])
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools_webrtc/perf/process_perf_results_test.py b/tools_webrtc/perf/process_perf_results_test.py
new file mode 100644
index 0000000..3aa5afd
--- /dev/null
+++ b/tools_webrtc/perf/process_perf_results_test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import os
+import sys
+
+import unittest
+from unittest import mock
+
+_SCRIPT_DIR = os.path.dirname(__file__)
+_SRC_DIR = os.path.normpath(os.path.join(_SCRIPT_DIR, '..', '..'))
+
+sys.path.insert(0, os.path.join(_SRC_DIR, 'third_party', 'protobuf', 'python'))
+import process_perf_results
+
+
+class ProcessPerfResultsTest(unittest.TestCase):
+ def testConfigurePythonPath(self):
+ # pylint: disable=protected-access
+ self.assertEqual(
+ 0,
+ process_perf_results._ConfigurePythonPath(
+ os.path.join(_SRC_DIR, 'out/Default')))
+
+ def testUploadToDasboard(self):
+ outdir = os.path.join(_SRC_DIR, 'out/Default')
+ args = mock.Mock(
+ build_properties='{' + '"outdir":"' + outdir + '", ' +
+ '"perf_dashboard_machine_group":"mock_machine_group", ' +
+ '"bot":"mock_bot", ' + '"webrtc_git_hash":"mock_webrtc_git_hash", ' +
+ '"commit_position":"123456", ' +
+ '"build_page_url":"mock_build_page_url", ' +
+ '"dashboard_url":"mock_dashboard_url"' + '}',
+ summary_json='mock_sumary_json',
+ task_output_dir='mock_task_output_dir',
+ test_suite='mock_test_suite',
+ )
+ perftest_output = mock.Mock(
+ absolute=lambda: 'dummy_path/perftest-output.pb',
+ is_file=lambda: True,
+ )
+ with mock.patch('pathlib.Path.rglob') as mocked_rglob:
+ with mock.patch('catapult_uploader.UploadToDashboard') as mocked_upload:
+ mocked_rglob.return_value = [perftest_output]
+ mocked_upload.return_value = 0
+ # pylint: disable=protected-access
+ self.assertEqual(0, process_perf_results._UploadToDasboard(args))
+
+ import catapult_uploader
+ mocked_upload.assert_called_once_with(
+ catapult_uploader.UploaderOptions(
+ perf_dashboard_machine_group='mock_machine_group',
+ bot='mock_bot',
+ test_suite='mock_test_suite',
+ webrtc_git_hash='mock_webrtc_git_hash',
+ commit_position='123456',
+ build_page_url='mock_build_page_url',
+ dashboard_url='mock_dashboard_url',
+ input_results_file=perftest_output.absolute()))
+
+
+if (__name__) == '__main__':
+ unittest.main()