forked from flutter/packages
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
1,932 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
secret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 0.0.4+1 | ||
|
||
- Moved to the `flutter/packages` repository |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Copyright 2014 The Flutter Authors. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without modification, | ||
are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above | ||
copyright notice, this list of conditions and the following | ||
disclaimer in the documentation and/or other materials provided | ||
with the distribution. | ||
* Neither the name of Google Inc. nor the names of its | ||
contributors may be used to endorse or promote products derived | ||
from this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Metrics Center | ||
|
||
Metrics center is a minimal set of code and services to support multiple perf | ||
metrics generators (e.g., Cocoon device lab, Cirrus bots, LUCI bots, Firebase | ||
Test Lab) and destinations (e.g., old Cocoon perf dashboard, Skia perf | ||
dashboard). The work and maintenance it requires is very close to that of just | ||
supporting a single generator and destination (e.g., engine bots to Skia perf), | ||
and the small amount of extra work is designed to make it easy to support more | ||
generators and destinations in the future. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
export 'src/common.dart'; | ||
export 'src/flutter.dart'; | ||
export 'src/google_benchmark.dart'; | ||
export 'src/skiaperf.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'dart:collection'; | ||
import 'dart:convert'; | ||
|
||
import 'package:crypto/crypto.dart'; | ||
import 'package:equatable/equatable.dart'; | ||
|
||
import 'package:googleapis_auth/auth.dart'; | ||
import 'package:googleapis_auth/auth_io.dart'; | ||
import 'package:http/http.dart'; | ||
|
||
/// Common format of a metric data point. | ||
class MetricPoint extends Equatable { | ||
/// Creates a new data point. | ||
MetricPoint( | ||
this.value, | ||
Map<String, String> tags, | ||
) : _tags = SplayTreeMap<String, String>.from(tags); | ||
|
||
/// Can store integer values. | ||
final double value; | ||
|
||
/// Test name, unit, timestamp, configs, git revision, ..., in sorted order. | ||
UnmodifiableMapView<String, String> get tags => | ||
UnmodifiableMapView<String, String>(_tags); | ||
|
||
/// Unique identifier for updating existing data point. | ||
/// | ||
/// We shouldn't have to worry about hash collisions until we have about | ||
/// 2^128 points. | ||
/// | ||
/// This id should stay constant even if the [tags.keys] are reordered. | ||
/// (Because we are using an ordered SplayTreeMap to generate the id.) | ||
String get id => sha256.convert(utf8.encode('$_tags')).toString(); | ||
|
||
@override | ||
String toString() { | ||
return 'MetricPoint(value=$value, tags=$_tags)'; | ||
} | ||
|
||
final SplayTreeMap<String, String> _tags; | ||
|
||
@override | ||
List<Object> get props => <Object>[value, tags]; | ||
} | ||
|
||
/// Interface to write [MetricPoint]. | ||
abstract class MetricDestination { | ||
/// Insert new data points or modify old ones with matching id. | ||
Future<void> update(List<MetricPoint> points); | ||
} | ||
|
||
/// Create `AuthClient` in case we only have an access token without the full | ||
/// credentials json. It's currently the case for Chrmoium LUCI bots. | ||
AuthClient authClientFromAccessToken(String token, List<String> scopes) { | ||
final DateTime anHourLater = DateTime.now().add(const Duration(hours: 1)); | ||
final AccessToken accessToken = | ||
AccessToken('Bearer', token, anHourLater.toUtc()); | ||
final AccessCredentials accessCredentials = | ||
AccessCredentials(accessToken, null, scopes); | ||
return authenticatedClient(Client(), accessCredentials); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
/// Strings used for MetricPoint tag keys | ||
const String kGithubRepoKey = 'gitRepo'; | ||
|
||
/// Strings used for MetricPoint tag keys | ||
const String kGitRevisionKey = 'gitRevision'; | ||
|
||
/// Strings used for MetricPoint tag keys | ||
const String kUnitKey = 'unit'; | ||
|
||
/// Strings used for MetricPoint tag keys | ||
const String kNameKey = 'name'; | ||
|
||
/// Strings used for MetricPoint tag keys | ||
const String kSubResultKey = 'subResult'; | ||
|
||
/// Flutter repo name. | ||
const String kFlutterFrameworkRepo = 'flutter/flutter'; | ||
|
||
/// Engine repo name. | ||
const String kFlutterEngineRepo = 'flutter/engine'; | ||
|
||
/// The key for the GCP project id in the credentials json. | ||
const String kProjectId = 'project_id'; | ||
|
||
/// Timeline key in JSON. | ||
const String kSourceTimeMicrosName = 'sourceTimeMicros'; | ||
|
||
/// The size of 500 is currently limited by Google datastore. It cannot write | ||
/// more than 500 entities in a single call. | ||
const int kMaxBatchSize = 500; | ||
|
||
/// The prod bucket name for Flutter's instance of Skia Perf. | ||
const String kBucketName = 'flutter-skia-perf-prod'; | ||
|
||
/// The test bucket name for Flutter's instance of Skia Perf. | ||
const String kTestBucketName = 'flutter-skia-perf-test'; | ||
|
||
/// JSON key for Skia Perf's git hash entry. | ||
const String kSkiaPerfGitHashKey = 'gitHash'; | ||
|
||
/// JSON key for Skia Perf's results entry. | ||
const String kSkiaPerfResultsKey = 'results'; | ||
|
||
/// JSON key for Skia Perf's value entry. | ||
const String kSkiaPerfValueKey = 'value'; | ||
|
||
/// JSON key for Skia Perf's options entry. | ||
const String kSkiaPerfOptionsKey = 'options'; | ||
|
||
/// JSON key for Skia Perf's default config entry. | ||
const String kSkiaPerfDefaultConfig = 'default'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'common.dart'; | ||
import 'constants.dart'; | ||
import 'legacy_datastore.dart'; | ||
import 'legacy_flutter.dart'; | ||
|
||
/// Convenient class to capture the benchmarks in the Flutter engine repo. | ||
class FlutterEngineMetricPoint extends MetricPoint { | ||
/// Creates a metric point for the Flutter engine repository. | ||
/// | ||
/// The `name`, `value`, and `gitRevision` parameters must not be null. | ||
FlutterEngineMetricPoint( | ||
String name, | ||
double value, | ||
String gitRevision, { | ||
Map<String, String> moreTags = const <String, String>{}, | ||
}) : super( | ||
value, | ||
<String, String>{ | ||
kNameKey: name, | ||
kGithubRepoKey: kFlutterEngineRepo, | ||
kGitRevisionKey: gitRevision, | ||
}..addAll(moreTags), | ||
); | ||
} | ||
|
||
/// All Flutter performance metrics (framework, engine, ...) should be written | ||
/// to this destination. | ||
class FlutterDestination extends MetricDestination { | ||
// TODO(liyuqian): change the implementation of this class (without changing | ||
// its public APIs) to remove `LegacyFlutterDestination` and directly use | ||
// `SkiaPerfDestination` once the migration is fully done. | ||
FlutterDestination._(this._legacyDestination); | ||
|
||
/// Creates a [FlutterDestination] from service account JSON. | ||
static Future<FlutterDestination> makeFromCredentialsJson( | ||
Map<String, dynamic> json) async { | ||
final LegacyFlutterDestination legacyDestination = | ||
LegacyFlutterDestination(await datastoreFromCredentialsJson(json)); | ||
return FlutterDestination._(legacyDestination); | ||
} | ||
|
||
/// Creates a [FlutterDestination] from an OAuth access token. | ||
static FlutterDestination makeFromAccessToken( | ||
String accessToken, String projectId) { | ||
final LegacyFlutterDestination legacyDestination = LegacyFlutterDestination( | ||
datastoreFromAccessToken(accessToken, projectId)); | ||
return FlutterDestination._(legacyDestination); | ||
} | ||
|
||
@override | ||
Future<void> update(List<MetricPoint> points) async { | ||
await _legacyDestination.update(points); | ||
} | ||
|
||
final LegacyFlutterDestination _legacyDestination; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'package:googleapis/storage/v1.dart'; | ||
import 'package:googleapis_auth/auth.dart'; | ||
|
||
/// Global (in terms of earth) mutex using Google Cloud Storage. | ||
class GcsLock { | ||
/// Create a lock with an authenticated client and a GCS bucket name. | ||
/// | ||
/// The client is used to communicate with Google Cloud Storage APIs. | ||
GcsLock(this._client, this._bucketName) | ||
: assert(_client != null), | ||
assert(_bucketName != null) { | ||
_api = StorageApi(_client); | ||
} | ||
|
||
/// Create a temporary lock file in GCS, and use it as a mutex mechanism to | ||
/// run a piece of code exclusively. | ||
/// | ||
/// There must be no existing lock file with the same name in order to | ||
/// proceed. If multiple [GcsLock]s with the same `bucketName` and | ||
/// `lockFileName` try [protectedRun] simultaneously, only one will proceed | ||
/// and create the lock file. All others will be blocked. | ||
/// | ||
/// When [protectedRun] finishes, the lock file is deleted, and other blocked | ||
/// [protectedRun] may proceed. | ||
/// | ||
/// If the lock file is stuck (e.g., `_unlock` is interrupted unexpectedly), | ||
/// one may need to manually delete the lock file from GCS to unblock any | ||
/// [protectedRun] that may depend on it. | ||
Future<void> protectedRun(String lockFileName, Future<void> f()) async { | ||
await _lock(lockFileName); | ||
try { | ||
await f(); | ||
} catch (e, stacktrace) { | ||
print(stacktrace); | ||
rethrow; | ||
} finally { | ||
await _unlock(lockFileName); | ||
} | ||
} | ||
|
||
Future<void> _lock(String lockFileName) async { | ||
final Object object = Object(); | ||
object.bucket = _bucketName; | ||
object.name = lockFileName; | ||
final Media content = Media(const Stream<List<int>>.empty(), 0); | ||
|
||
Duration waitPeriod = const Duration(milliseconds: 10); | ||
bool locked = false; | ||
while (!locked) { | ||
try { | ||
await _api.objects.insert(object, _bucketName, | ||
ifGenerationMatch: '0', uploadMedia: content); | ||
locked = true; | ||
} on DetailedApiRequestError catch (e) { | ||
if (e.status == 412) { | ||
// Status 412 means that the lock file already exists. Wait until | ||
// that lock file is deleted. | ||
await Future<void>.delayed(waitPeriod); | ||
waitPeriod *= 2; | ||
if (waitPeriod >= _kWarningThreshold) { | ||
print( | ||
'The lock is waiting for a long time: $waitPeriod. ' | ||
'If the lock file $lockFileName in bucket $_bucketName ' | ||
'seems to be stuck (i.e., it was created a long time ago and ' | ||
'no one seems to be owning it currently), delete it manually ' | ||
'to unblock this.', | ||
); | ||
} | ||
} else { | ||
rethrow; | ||
} | ||
} | ||
} | ||
} | ||
|
||
Future<void> _unlock(String lockFileName) async { | ||
await _api.objects.delete(_bucketName, lockFileName); | ||
} | ||
|
||
StorageApi _api; | ||
|
||
final String _bucketName; | ||
final AuthClient _client; | ||
|
||
static const Duration _kWarningThreshold = Duration(seconds: 10); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'package:github/github.dart'; | ||
|
||
/// Singleton class to query some Github info with an in-memory cache. | ||
class GithubHelper { | ||
/// Return the singleton helper. | ||
factory GithubHelper() { | ||
return _singleton; | ||
} | ||
|
||
GithubHelper._internal(); | ||
|
||
/// The result is cached in memory so querying the same thing again in the | ||
/// same process is fast. | ||
/// | ||
/// Our unit test requires that calling this method 1000 times for the same | ||
/// `githubRepo` and `sha` should be done in 1 second. | ||
Future<DateTime> getCommitDateTime(String githubRepo, String sha) async { | ||
final String key = '$githubRepo/commit/$sha'; | ||
if (_commitDateTimeCache[key] == null) { | ||
final RepositoryCommit commit = await _github.repositories | ||
.getCommit(RepositorySlug.full(githubRepo), sha); | ||
_commitDateTimeCache[key] = commit.commit.committer.date; | ||
} | ||
return _commitDateTimeCache[key]; | ||
} | ||
|
||
static final GithubHelper _singleton = GithubHelper._internal(); | ||
|
||
final GitHub _github = GitHub(auth: findAuthenticationFromEnvironment()); | ||
final Map<String, DateTime> _commitDateTimeCache = <String, DateTime>{}; | ||
} |
Oops, something went wrong.