/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.rules;
import com.facebook.buck.artifact_cache.CacheResult;
import com.facebook.buck.event.AbstractBuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.EventKey;
import com.facebook.buck.event.RuleKeyCalculationEvent;
import com.facebook.buck.event.WorkAdvanceEvent;
import com.facebook.buck.log.views.JsonViews;
import com.facebook.buck.model.BuildId;
import com.facebook.buck.rules.keys.RuleKeyFactory;
import com.facebook.buck.timing.ClockDuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.base.Preconditions;
import com.google.common.hash.HashCode;
import java.util.Optional;
import javax.annotation.Nullable;
/** Base class for events about build rules. */
public abstract class BuildRuleEvent extends AbstractBuckEvent implements WorkAdvanceEvent {
private final BuildRule rule;
/** Accumulated duration of work spent on this rule up until this event occurred. */
@Nullable protected ClockDuration duration;
protected BuildRuleEvent(EventKey eventKey, BuildRule rule) {
super(eventKey);
this.rule = rule;
}
@JsonView(JsonViews.MachineReadableLog.class)
public BuildRule getBuildRule() {
return rule;
}
public ClockDuration getDuration() {
Preconditions.checkState(isConfigured(), "Event was not configured yet.");
return Preconditions.checkNotNull(duration);
}
@Override
public String getValueString() {
return rule.getFullyQualifiedName();
}
@Override
public final String getEventName() {
return "BuildRule" + getClass().getSimpleName();
}
public abstract boolean isRuleRunningAfterThisEvent();
private static EventKey createEventKey(BuildRule rule) {
return EventKey.slowValueKey("BuildRuleEvent", rule.getFullyQualifiedName());
}
public static Started started(BuildRule rule, BuildRuleDurationTracker tracker) {
return new Started(createEventKey(rule), rule, tracker);
}
public static Finished finished(
BeginningBuildRuleEvent beginning,
BuildRuleKeys ruleKeys,
BuildRuleStatus status,
CacheResult cacheResult,
Optional<BuildId> origin,
Optional<BuildRuleSuccessType> successType,
Optional<HashCode> outputHash,
Optional<Long> outputSize,
Optional<BuildRuleDiagnosticData> diagnosticData) {
return new Finished(
beginning,
ruleKeys,
status,
cacheResult,
origin,
successType,
outputHash,
outputSize,
diagnosticData);
}
public static StartedRuleKeyCalc ruleKeyCalculationStarted(
BuildRule rule, BuildRuleDurationTracker tracker) {
return new StartedRuleKeyCalc(createEventKey(rule), rule, tracker);
}
public static FinishedRuleKeyCalc ruleKeyCalculationFinished(
StartedRuleKeyCalc started, RuleKeyFactory<RuleKey> ruleKeyFactory) {
return new FinishedRuleKeyCalc(started, ruleKeyFactory);
}
public static Suspended suspended(
BeginningBuildRuleEvent beginning, RuleKeyFactory<RuleKey> ruleKeyFactory) {
return new Suspended(beginning, ruleKeyFactory);
}
public static Resumed resumed(
BuildRule rule, BuildRuleDurationTracker tracker, RuleKeyFactory<RuleKey> ruleKeyFactory) {
return new Resumed(createEventKey(rule), rule, tracker, ruleKeyFactory);
}
public static WillBuildLocally willBuildLocally(BuildRule rule) {
return new WillBuildLocally(rule);
}
/**
* A {@link BuildRuleEvent} that denotes beginning of computation for a particular {@link
* BuildRule}.
*/
public abstract static class BeginningBuildRuleEvent extends BuildRuleEvent {
@JsonIgnore private final BuildRuleDurationTracker tracker;
public BeginningBuildRuleEvent(
EventKey eventKey, BuildRule rule, BuildRuleDurationTracker tracker) {
super(eventKey, rule);
this.tracker = tracker;
}
@Override
public void configure(
long timestamp, long nanoTime, long threadUserNanoTime, long threadId, BuildId buildId) {
super.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId);
this.duration = tracker.doBeginning(getBuildRule(), timestamp, nanoTime);
}
@Override
public final boolean isRuleRunningAfterThisEvent() {
return true;
}
}
/**
* A {@link BuildRuleEvent} that denotes ending of computation for a particular {@link BuildRule}.
*/
public abstract static class EndingBuildRuleEvent extends BuildRuleEvent {
@JsonIgnore private final BeginningBuildRuleEvent beginning;
public EndingBuildRuleEvent(BeginningBuildRuleEvent beginning) {
super(beginning.getEventKey(), beginning.getBuildRule());
this.beginning = beginning;
}
@JsonIgnore
public BeginningBuildRuleEvent getBeginningEvent() {
return beginning;
}
@Override
public void configure(
long timestamp, long nanoTime, long threadUserNanoTime, long threadId, BuildId buildId) {
super.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId);
long threadUserNanoDuration = threadUserNanoTime - beginning.getThreadUserNanoTime();
this.duration =
beginning.tracker.doEnding(getBuildRule(), timestamp, nanoTime, threadUserNanoDuration);
}
@Override
public final boolean isRuleRunningAfterThisEvent() {
return false;
}
}
/**
* Marks the start of processing a build rule. We keep this as a distinct base class for
* subscribers who want to process start/rule-key-calc events separately.
*/
public static class Started extends BeginningBuildRuleEvent {
private Started(EventKey eventKey, BuildRule rule, BuildRuleDurationTracker tracker) {
super(eventKey, rule, tracker);
}
}
/** Marks the end of processing a build rule. */
public static class Finished extends EndingBuildRuleEvent {
private final BuildRuleStatus status;
private final CacheResult cacheResult;
private final Optional<BuildId> origin;
private final Optional<BuildRuleSuccessType> successType;
private final BuildRuleKeys ruleKeys;
private final Optional<HashCode> outputHash;
private final Optional<Long> outputSize;
Optional<BuildRuleDiagnosticData> diagnosticData;
private Finished(
BeginningBuildRuleEvent beginning,
BuildRuleKeys ruleKeys,
BuildRuleStatus status,
CacheResult cacheResult,
Optional<BuildId> origin,
Optional<BuildRuleSuccessType> successType,
Optional<HashCode> outputHash,
Optional<Long> outputSize,
Optional<BuildRuleDiagnosticData> diagnosticData) {
super(beginning);
this.status = status;
this.cacheResult = cacheResult;
this.origin = origin;
this.successType = successType;
this.ruleKeys = ruleKeys;
this.outputHash = outputHash;
this.outputSize = outputSize;
this.diagnosticData = diagnosticData;
}
@JsonView(JsonViews.MachineReadableLog.class)
public BuildRuleStatus getStatus() {
return status;
}
@JsonView(JsonViews.MachineReadableLog.class)
public CacheResult getCacheResult() {
return cacheResult;
}
@JsonView(JsonViews.MachineReadableLog.class)
public Optional<BuildId> getOrigin() {
return origin;
}
@JsonIgnore
public Optional<BuildRuleSuccessType> getSuccessType() {
return successType;
}
@JsonView(JsonViews.MachineReadableLog.class)
public BuildRuleKeys getRuleKeys() {
return ruleKeys;
}
@JsonView(JsonViews.MachineReadableLog.class)
public Optional<HashCode> getOutputHash() {
return outputHash;
}
@JsonIgnore
public Optional<Long> getOutputSize() {
return outputSize;
}
@JsonIgnore
public Optional<BuildRuleDiagnosticData> getDiagnosticData() {
return diagnosticData;
}
@Override
public String toString() {
String success = successType.isPresent() ? successType.get().toString() : "MISSING";
return String.format(
"BuildRuleFinished(%s): %s %s %s %s%s",
getBuildRule().getFullyQualifiedName(),
getStatus(),
getCacheResult(),
success,
getRuleKeys().getRuleKey().toString(),
getRuleKeys().getInputRuleKey().isPresent()
? " I" + getRuleKeys().getInputRuleKey().get().toString()
: "");
}
@JsonIgnore
public String getResultString() {
switch (getStatus()) {
case SUCCESS:
return getSuccessType().get().getShortDescription();
case FAIL:
return "FAILED";
case CANCELED:
return "CANCEL";
default:
return getStatus().toString();
}
}
}
/** Marks a rule is suspended from processing. */
public static class Suspended extends EndingBuildRuleEvent {
private final String ruleKey;
private Suspended(BeginningBuildRuleEvent beginning, RuleKeyFactory<RuleKey> ruleKeyFactory) {
super(beginning);
this.ruleKey = ruleKeyFactory.build(beginning.getBuildRule()).toString();
}
@JsonIgnore
public String getRuleKey() {
return ruleKey;
}
}
/** Marks the continuation of processing a rule. */
public static class Resumed extends BeginningBuildRuleEvent {
private final String ruleKey;
private Resumed(
EventKey eventKey,
BuildRule rule,
BuildRuleDurationTracker tracker,
RuleKeyFactory<RuleKey> ruleKeyFactory) {
super(eventKey, rule, tracker);
this.ruleKey = ruleKeyFactory.build(rule).toString();
}
@JsonIgnore
public String getRuleKey() {
return ruleKey;
}
}
/**
* Marks the start of processing a rule to calculate its rule key. We overload this as both a rule
* start and rule key calc start event to generate less events and be more efficient.
*/
private static class StartedRuleKeyCalc extends Started
implements RuleKeyCalculationEvent.Started {
private StartedRuleKeyCalc(
EventKey eventKey, BuildRule rule, BuildRuleDurationTracker tracker) {
super(eventKey, rule, tracker);
}
@Override
public Type getType() {
return Type.NORMAL;
}
}
/**
* Marks the completion of processing a rule to calculate its rule key. We overload this as both a
* rule suspend and rule key calc finish event to generate less events and be more efficient.
*/
private static class FinishedRuleKeyCalc extends Suspended
implements RuleKeyCalculationEvent.Finished {
private FinishedRuleKeyCalc(
StartedRuleKeyCalc started, RuleKeyFactory<RuleKey> ruleKeyFactory) {
super(started, ruleKeyFactory);
}
@Override
public Type getType() {
return Type.NORMAL;
}
}
/** Denotes that a particular build rule will be built locally. */
public static class WillBuildLocally extends AbstractBuckEvent implements WorkAdvanceEvent {
private final BuildRule rule;
public WillBuildLocally(BuildRule rule) {
super(EventKey.unique());
this.rule = rule;
}
@Override
public String getEventName() {
return "BuildRuleWillBuildLocally";
}
@Override
protected String getValueString() {
return rule.toString();
}
}
public static Scope ruleKeyCalculationScope(
BuckEventBus eventBus,
BuildRule rule,
BuildRuleDurationTracker tracker,
RuleKeyFactory<RuleKey> ruleKeyFactory) {
StartedRuleKeyCalc started = ruleKeyCalculationStarted(rule, tracker);
eventBus.post(started);
return () -> eventBus.post(ruleKeyCalculationFinished(started, ruleKeyFactory));
}
public static Scope resumeSuspendScope(
BuckEventBus eventBus,
BuildRule rule,
BuildRuleDurationTracker tracker,
RuleKeyFactory<RuleKey> ruleKeyFactory) {
Resumed resumed = resumed(rule, tracker, ruleKeyFactory);
eventBus.post(resumed);
return () -> eventBus.post(suspended(resumed, ruleKeyFactory));
}
}