blob: beb598a2dc5b39d01048d646a79cc7a9354f08c0 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@experimental
library build_runner.src.generate.performance_tracker;
import 'dart:async';
import 'package:build/build.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';
import 'package:timing/timing.dart';
import 'phase.dart';
part 'performance_tracker.g.dart';
/// The [TimeSlice] of an entire build, including all its [actions].
@JsonSerializable()
class BuildPerformance extends TimeSlice {
/// The [TimeSlice] of each phase ran in this build.
final Iterable<BuildPhasePerformance> phases;
/// The [TimeSlice] of running an individual [Builder] on an individual input.
final Iterable<BuilderActionPerformance> actions;
BuildPerformance(
this.phases, this.actions, DateTime startTime, DateTime stopTime)
: super(startTime, stopTime);
factory BuildPerformance.fromJson(Map<String, dynamic> json) =>
_$BuildPerformanceFromJson(json);
@override
Map<String, dynamic> toJson() => _$BuildPerformanceToJson(this);
}
/// The [TimeSlice] of a full [BuildPhase] within a larger build.
@JsonSerializable()
class BuildPhasePerformance extends TimeSlice {
final List<String> builderKeys;
BuildPhasePerformance(this.builderKeys, DateTime startTime, DateTime stopTime)
: super(startTime, stopTime);
factory BuildPhasePerformance.fromJson(Map<String, dynamic> json) =>
_$BuildPhasePerformanceFromJson(json);
@override
Map<String, dynamic> toJson() => _$BuildPhasePerformanceToJson(this);
}
/// The [TimeSlice] of a [builderKey] running on [primaryInput] within a build.
@JsonSerializable()
class BuilderActionPerformance extends TimeSlice {
final String builderKey;
@JsonKey(fromJson: _assetIdFromJson, toJson: _assetIdToJson)
final AssetId primaryInput;
final Iterable<BuilderActionStagePerformance> stages;
Duration get innerDuration => stages.fold(
Duration.zero, (duration, stage) => duration + stage.innerDuration);
BuilderActionPerformance(this.builderKey, this.primaryInput, this.stages,
DateTime startTime, DateTime stopTime)
: super(startTime, stopTime);
factory BuilderActionPerformance.fromJson(Map<String, dynamic> json) =>
_$BuilderActionPerformanceFromJson(json);
@override
Map<String, dynamic> toJson() => _$BuilderActionPerformanceToJson(this);
}
/// The [TimeSlice] of a particular task within a builder action.
///
/// This is some slice of overall [BuilderActionPerformance].
@JsonSerializable()
class BuilderActionStagePerformance extends TimeSliceGroup {
final String label;
BuilderActionStagePerformance(this.label, List<TimeSlice> slices)
: super(slices);
factory BuilderActionStagePerformance.fromJson(Map<String, dynamic> json) =>
_$BuilderActionStagePerformanceFromJson(json);
@override
Map<String, dynamic> toJson() => _$BuilderActionStagePerformanceToJson(this);
}
/// Interface for tracking the overall performance of a build.
abstract class BuildPerformanceTracker
implements TimeTracker, BuildPerformance {
/// Tracks [runPhase] which performs [action] on all inputs in a phase, and
/// return the outputs generated.
Future<Iterable<AssetId>> trackBuildPhase(
BuildPhase action, Future<Iterable<AssetId>> Function() runPhase);
/// Returns a [BuilderActionTracker] for tracking [builderKey] on
/// [primaryInput] and adds it to [actions].
BuilderActionTracker addBuilderAction(
AssetId primaryInput, String builderKey);
factory BuildPerformanceTracker() => _BuildPerformanceTrackerImpl();
/// A [BuildPerformanceTracker] with as little overhead as possible. Does no
/// actual tracking and does not implement many fields/methods.
factory BuildPerformanceTracker.noOp() =>
_NoOpBuildPerformanceTracker.sharedInstance;
}
/// Real implementation of [BuildPerformanceTracker].
///
/// Use [BuildPerformanceTracker] factory to get an instance.
class _BuildPerformanceTrackerImpl extends SimpleAsyncTimeTracker
implements BuildPerformanceTracker {
@override
Iterable<BuildPhaseTracker> get phases => _phases;
final _phases = <BuildPhaseTracker>[];
@override
Iterable<BuilderActionTracker> get actions => _actions;
final _actions = <BuilderActionTracker>[];
/// Tracks [action] which is ran with [runPhase].
///
/// This represents running a [Builder] on a group of sources.
///
/// Returns all the outputs generated by the phase.
@override
Future<Iterable<AssetId>> trackBuildPhase(
BuildPhase action, Future<Iterable<AssetId>> Function() runPhase) {
assert(isTracking);
var tracker = BuildPhaseTracker(action);
_phases.add(tracker);
return tracker.track(runPhase);
}
/// Returns a new [BuilderActionTracker] and adds it to [actions].
///
/// The [BuilderActionTracker] will already be started, but you must stop it.
@override
BuilderActionTracker addBuilderAction(
AssetId primaryInput, String builderKey) {
assert(isTracking);
var tracker = BuilderActionTracker(primaryInput, builderKey);
_actions.add(tracker);
return tracker;
}
@override
Map<String, dynamic> toJson() => _$BuildPerformanceToJson(this);
}
/// No-op implementation of [BuildPerformanceTracker].
///
/// Read-only fields will throw, and tracking methods just directly invoke their
/// closures without tracking anything.
///
/// Use [BuildPerformanceTracker.noOp] to get an instance.
class _NoOpBuildPerformanceTracker extends NoOpTimeTracker
implements BuildPerformanceTracker {
static final _NoOpBuildPerformanceTracker sharedInstance =
_NoOpBuildPerformanceTracker();
@override
Iterable<BuilderActionTracker> get actions => throw UnimplementedError();
@override
Iterable<BuildPhaseTracker> get phases => throw UnimplementedError();
@override
BuilderActionTracker addBuilderAction(
AssetId primaryInput, String builderKey) =>
BuilderActionTracker.noOp();
@override
Future<Iterable<AssetId>> trackBuildPhase(
BuildPhase action, Future<Iterable<AssetId>> Function() runPhase) =>
runPhase();
@override
Map<String, dynamic> toJson() => _$BuildPerformanceToJson(this);
}
/// Internal class that tracks the [TimeSlice] of an entire [BuildPhase].
///
/// Tracks total time it took to run on all inputs available to that action.
///
/// Use [track] to start actually tracking an operation.
///
/// This is only meaningful for non-lazy phases.
class BuildPhaseTracker extends SimpleAsyncTimeTracker
implements BuildPhasePerformance {
@override
final List<String> builderKeys;
BuildPhaseTracker(BuildPhase action)
: builderKeys = action is InBuildPhase
? [action.builderLabel]
: (action as PostBuildPhase)
.builderActions
.map((action) => action.builderLabel)
.toList();
@override
Map<String, dynamic> toJson() => _$BuildPhasePerformanceToJson(this);
}
/// Interface for tracking the [TimeSlice] of an individual [Builder] on a given
/// primary input.
abstract class BuilderActionTracker
implements TimeTracker, BuilderActionPerformance, StageTracker {
/// Tracks the time of [runStage] and associates it with [label].
///
/// You can specify [runStage] as [isExternal] (waiting for some external
/// resource like network, process or file IO). In that case [runStage] will
/// be tracked as single time slice from the beginning of the stage till
/// completion of Future returned by [runStage].
///
/// Otherwise all separate time slices of asynchronous execution will be
/// tracked, but waiting for external resources will be a gap.
@override
T trackStage<T>(String label, T Function() runStage,
{bool isExternal = false});
factory BuilderActionTracker(AssetId primaryInput, String builderKey) =>
_BuilderActionTrackerImpl(primaryInput, builderKey);
/// A [BuilderActionTracker] with as little overhead as possible. Does no
/// actual tracking and does not implement many fields/methods.
factory BuilderActionTracker.noOp() =>
_NoOpBuilderActionTracker._sharedInstance;
}
/// Real implementation of [BuilderActionTracker] which records timings.
///
/// Use the [BuilderActionTracker] factory to get an instance.
class _BuilderActionTrackerImpl extends SimpleAsyncTimeTracker
implements BuilderActionTracker {
@override
final String builderKey;
@override
final AssetId primaryInput;
@override
final List<BuilderActionStageTracker> stages = [];
@override
Duration get innerDuration => stages.fold(
Duration.zero, (duration, stage) => duration + stage.innerDuration);
_BuilderActionTrackerImpl(this.primaryInput, this.builderKey);
@override
T trackStage<T>(String label, T Function() action,
{bool isExternal = false}) {
var tracker = isExternal
? BuilderActionStageSimpleTracker(label)
: BuilderActionStageAsyncTracker(label);
stages.add(tracker);
return tracker.track(action);
}
@override
Map<String, dynamic> toJson() => _$BuilderActionPerformanceToJson(this);
}
/// No-op instance of [BuilderActionTracker] which does nothing and throws an
/// unimplemented error for everything but [trackStage], which delegates directly to
/// the wrapped function.
///
/// Use the [BuilderActionTracker.noOp] factory to get an instance.
class _NoOpBuilderActionTracker extends NoOpTimeTracker
implements BuilderActionTracker {
static final _NoOpBuilderActionTracker _sharedInstance =
_NoOpBuilderActionTracker();
@override
String get builderKey => throw UnimplementedError();
@override
Duration get duration => throw UnimplementedError();
@override
Iterable<BuilderActionStagePerformance> get stages =>
throw UnimplementedError();
@override
Duration get innerDuration => throw UnimplementedError();
@override
AssetId get primaryInput => throw UnimplementedError();
@override
T trackStage<T>(String label, T Function() runStage,
{bool isExternal = false}) =>
runStage();
@override
Map<String, dynamic> toJson() => _$BuilderActionPerformanceToJson(this);
}
/// Tracks the [TimeSliceGroup] of an individual task.
///
/// These represent a slice of the [BuilderActionPerformance].
abstract class BuilderActionStageTracker
implements BuilderActionStagePerformance {
T track<T>(T Function() action);
}
class BuilderActionStageAsyncTracker extends AsyncTimeTracker
implements BuilderActionStageTracker {
@override
final String label;
BuilderActionStageAsyncTracker(this.label) : super(trackNested: false);
@override
Map<String, dynamic> toJson() => _$BuilderActionStagePerformanceToJson(this);
}
class BuilderActionStageSimpleTracker extends BuilderActionStagePerformance
implements BuilderActionStageTracker {
final _tracker = SimpleAsyncTimeTracker();
BuilderActionStageSimpleTracker(String label) : super(label, []) {
slices.add(_tracker);
}
@override
T track<T>(T Function() action) => _tracker.track(action);
}
AssetId _assetIdFromJson(String json) => AssetId.parse(json);
String _assetIdToJson(AssetId id) => id.toString();