Skip to content

Commit

Permalink
[DataProvider] Add SkymeldUsed Datum and provide it (#116)
Browse files Browse the repository at this point in the history
As a first step to supporting Bazel profiles where Skymeld was used, add
a `Datum` that indices whether the profile looks like it was generated
while using Skymeld.

Contributes to #91
Contributes to #97

---------

Signed-off-by: Sara Adams <sara.e.adams@gmail.com>
  • Loading branch information
saraadams authored Nov 24, 2023
1 parent a595c26 commit f34ee71
Show file tree
Hide file tree
Showing 15 changed files with 453 additions and 247 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
public enum BazelProfilePhase {
// The order of these is important, it reflects in which order we expect the timestamps of the
// phases markers in the Bazel profile to be.
// Names taken from
// https://github.com/bazelbuild/bazel/blob/febfa54dbe62d719ad6dbbfdd12bd6f8c0923b7a/src/main/java/com/google/devtools/build/lib/profiler/ProfilePhase.java#L20-L32
LAUNCH("Launch Blaze"),
INIT("Initialize command"),
EVALUATE("Evaluate target patterns"),
DEPENDENCIES("Load and analyze dependencies"),
TARGET_PATTERN_EVAL("Evaluate target patterns"),
ANALYZE("Load and analyze dependencies"),
ANALYZE_AND_EXECUTE("Load, analyze dependencies and build artifacts"),
LICENSE("Analyze licenses"),
PREPARE("Prepare for build"),
EXECUTE("Build artifacts"),
FINISH("Complete build");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ TYPES = [
"EstimatedJobsFlagValue.java",
"GarbageCollectionStats.java",
"MergedEventsPresent.java",
"SkymeldUsed.java",
"TotalDuration.java",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ public static List<DataProvider> getAllDataProviders() {
new CriticalPathDurationDataProvider(),
new EstimatedCoresDataProvider(),
new GarbageCollectionStatsDataProvider(),
new MergedEventsPresentDataProvider(),
new LocalActionsDataProvider(),
new MergedEventsPresentDataProvider(),
new SkymeldUsedDataProvider(),

// RemoteExecution
new CriticalPathQueuingDurationDataProvider(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ EstimatedJobsFlagValue getEstimatedFlagValueJobs()
if (evaluateAndDependenciesPhaseSkyframeEvaluators == null) {
return new EstimatedJobsFlagValue(ESTIMATED_JOBS_FLAG_VALUE_EMPTY_REASON_EVAL_DEP_MISSING);
}
if (executionPhaseSkyframeEvaluators == null
if (executionPhaseSkyframeEvaluatorsMaxValue == null
|| evaluateAndDependenciesPhaseSkyframeEvaluatorsMaxValue == null) {
return new EstimatedJobsFlagValue(ESTIMATED_JOBS_FLAG_VALUE_EMPTY_REASON_EVENTS_MISSING);
}
Expand Down Expand Up @@ -171,13 +171,13 @@ private synchronized void determineEstimatedCoresAvailable()
// phases. These phases should use as many cores as there are available, irrespective of
// whether the Bazel flag `--jobs` is set or not.
Optional<BazelPhaseDescription> start =
bazelPhaseDescriptions.has(BazelProfilePhase.EVALUATE)
? bazelPhaseDescriptions.get(BazelProfilePhase.EVALUATE)
: bazelPhaseDescriptions.get(BazelProfilePhase.DEPENDENCIES);
bazelPhaseDescriptions.has(BazelProfilePhase.TARGET_PATTERN_EVAL)
? bazelPhaseDescriptions.get(BazelProfilePhase.TARGET_PATTERN_EVAL)
: bazelPhaseDescriptions.get(BazelProfilePhase.ANALYZE);
Optional<BazelPhaseDescription> end =
bazelPhaseDescriptions.has(BazelProfilePhase.DEPENDENCIES)
? bazelPhaseDescriptions.get(BazelProfilePhase.DEPENDENCIES)
: bazelPhaseDescriptions.get(BazelProfilePhase.EVALUATE);
bazelPhaseDescriptions.has(BazelProfilePhase.ANALYZE)
? bazelPhaseDescriptions.get(BazelProfilePhase.ANALYZE)
: bazelPhaseDescriptions.get(BazelProfilePhase.TARGET_PATTERN_EVAL);
if (start.isEmpty() || end.isEmpty()) {
// The profile does not include that data necessary.
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2023 EngFlow 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.engflow.bazel.invocation.analyzer.dataproviders;

import com.engflow.bazel.invocation.analyzer.core.Datum;

/**
* Whether evidence was found that Skymeld was used.
*
* @see <a href="https://github.com/bazelbuild/bazel/issues/14057">Project Skymeld GitHub issue</a>
*/
public class SkymeldUsed implements Datum {
private final boolean skymeldUsed;

public SkymeldUsed(boolean skymeldUsed) {
this.skymeldUsed = skymeldUsed;
}

public boolean isSkymeldUsed() {
return skymeldUsed;
}

@Override
public boolean isEmpty() {
return false;
}

@Override
public String getEmptyReason() {
return null;
}

@Override
public String getDescription() {
return "Whether the Bazel Profile includes events indicating that Skymeld was used.";
}

@Override
public String getSummary() {
return String.valueOf(skymeldUsed);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2022 EngFlow 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.engflow.bazel.invocation.analyzer.dataproviders;

import com.engflow.bazel.invocation.analyzer.bazelprofile.BazelProfilePhase;
import com.engflow.bazel.invocation.analyzer.core.DataProvider;
import com.engflow.bazel.invocation.analyzer.core.DatumSupplier;
import com.engflow.bazel.invocation.analyzer.core.DatumSupplierSpecification;
import com.engflow.bazel.invocation.analyzer.core.InvalidProfileException;
import com.engflow.bazel.invocation.analyzer.core.MissingInputException;
import com.engflow.bazel.invocation.analyzer.core.NullDatumException;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;

/**
* A {@link DataProvider} that returns whether the profile looks like it was generated while using
* Skymeld.
*
* @see <a href="https://github.com/bazelbuild/bazel/issues/14057">Project Skymeld GitHub issue</a>
*/
public class SkymeldUsedDataProvider extends DataProvider {

@Override
public List<DatumSupplierSpecification<?>> getSuppliers() {
return List.of(
DatumSupplierSpecification.of(
SkymeldUsed.class, DatumSupplier.memoized(this::getSkymeldUsed)));
}

@VisibleForTesting
SkymeldUsed getSkymeldUsed()
throws InvalidProfileException, MissingInputException, NullDatumException {
BazelPhaseDescriptions bazelPhaseDescriptions =
getDataManager().getDatum(BazelPhaseDescriptions.class);
return new SkymeldUsed(
bazelPhaseDescriptions.get(BazelProfilePhase.ANALYZE_AND_EXECUTE).isPresent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ public class NegligiblePhaseSuggestionProvider extends SuggestionProviderBase {
@VisibleForTesting
public static final List<BazelProfilePhase> NON_NEGLIGIBLE_PHASES =
List.of(
BazelProfilePhase.EVALUATE, BazelProfilePhase.DEPENDENCIES, BazelProfilePhase.EXECUTE);
BazelProfilePhase.TARGET_PATTERN_EVAL,
BazelProfilePhase.ANALYZE,
BazelProfilePhase.ANALYZE_AND_EXECUTE,
BazelProfilePhase.EXECUTE);
// Don't apply this check to profiles shorter than this duration
private static final Duration MIN_DURATION = Duration.ofSeconds(30);
// Non-negligible phases should be less than this percentage of total duration (1.0 = 1%)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -122,78 +123,37 @@ protected BazelProfile useProfile(WriteBazelProfile.ProfileSection... profileSec

/**
* Create the Bazel profile events that establish the phases of invocation processing. For a valid
* profile, at least launchStart and finishTime have to be non-null.
* profile, at least {@link BazelProfilePhase#LAUNCH} has to be included in `startTimes`, and
* `finishTime` has to be non-null.
*
* @param launchStart timestamp of the Launch Blaze event
* @param initStart timestamp of the Initialize command event
* @param evalStart timestamp of the Evaluate target patterns event
* @param depStart timestamp of the Load and analyze dependencies event
* @param prepStart timestamp of the Prepare for build event
* @param execStart timestamp of the Build artifacts event
* @param finishStart timestamp of the Complete build event
* @param startTimes map of start times of the Bazel profile phases to include
* @param finishTime timestamp of the Finishing event
* @return array of phase marker events
*/
protected WriteBazelProfile.ThreadEvent[] createPhaseEvents(
@Nullable Timestamp launchStart,
@Nullable Timestamp initStart,
@Nullable Timestamp evalStart,
@Nullable Timestamp depStart,
@Nullable Timestamp prepStart,
@Nullable Timestamp execStart,
@Nullable Timestamp finishStart,
@Nullable Timestamp finishTime) {
Map<BazelProfilePhase, Timestamp> startTimes, @Nullable Timestamp finishTime) {
List<WriteBazelProfile.ThreadEvent> threadEvents = new ArrayList<>();
if (launchStart != null) {
threadEvents.add(
complete(
BazelProfilePhase.LAUNCH.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
launchStart,
TimeUtil.getDurationBetween(
launchStart, initStart == null ? Timestamp.ofMicros(0) : initStart)));
}
if (initStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.INIT.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
initStart));
}
if (evalStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.EVALUATE.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
evalStart));
}
if (depStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.DEPENDENCIES.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
depStart));
}
if (prepStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.PREPARE.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
prepStart));
}
if (execStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.EXECUTE.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
execStart));
}
if (finishStart != null) {
threadEvents.add(
instant(
BazelProfilePhase.FINISH.name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
finishStart));
for (var entry : startTimes.entrySet()) {
switch (entry.getKey()) {
case LAUNCH:
threadEvents.add(
complete(
entry.getKey().name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
entry.getValue(),
TimeUtil.getDurationBetween(
entry.getValue(),
startTimes.containsKey(BazelProfilePhase.INIT)
? startTimes.get(BazelProfilePhase.INIT)
: Timestamp.ofMicros(0))));
break;
default:
threadEvents.add(
instant(
entry.getKey().name,
BazelProfileConstants.CAT_BUILD_PHASE_MARKER,
entry.getValue()));
}
}
if (finishTime != null) {
threadEvents.add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ public void getOrClosestBeforeShouldReturnSelf() {
BazelPhaseDescription description =
new BazelPhaseDescription(Timestamp.ofMicros(1), Timestamp.ofMicros(2));
BazelPhaseDescriptions descriptions =
BazelPhaseDescriptions.newBuilder().add(BazelProfilePhase.EVALUATE, description).build();
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.EVALUATE).get())
BazelPhaseDescriptions.newBuilder()
.add(BazelProfilePhase.TARGET_PATTERN_EVAL, description)
.build();
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.TARGET_PATTERN_EVAL).get())
.isEqualTo(description);
}

Expand All @@ -40,26 +42,29 @@ public void getOrClosestBeforeShouldReturnEarlier() {
BazelPhaseDescriptions descriptions =
BazelPhaseDescriptions.newBuilder()
.add(BazelProfilePhase.INIT, otherDescription)
.add(BazelProfilePhase.EVALUATE, expectedDescription)
.add(BazelProfilePhase.TARGET_PATTERN_EVAL, expectedDescription)
.add(BazelProfilePhase.PREPARE, otherDescription)
.build();
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.DEPENDENCIES).get())
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.ANALYZE).get())
.isEqualTo(expectedDescription);
}

@Test
public void getOrClosestBeforeShouldReturnEmpty() {
BazelPhaseDescriptions descriptions = BazelPhaseDescriptions.newBuilder().build();
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.EVALUATE).isEmpty()).isTrue();
assertThat(descriptions.getOrClosestBefore(BazelProfilePhase.TARGET_PATTERN_EVAL).isEmpty())
.isTrue();
}

@Test
public void getOrClosestAfterShouldReturnSelf() {
BazelPhaseDescription description =
new BazelPhaseDescription(Timestamp.ofMicros(1), Timestamp.ofMicros(2));
BazelPhaseDescriptions descriptions =
BazelPhaseDescriptions.newBuilder().add(BazelProfilePhase.EVALUATE, description).build();
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.EVALUATE).get())
BazelPhaseDescriptions.newBuilder()
.add(BazelProfilePhase.TARGET_PATTERN_EVAL, description)
.build();
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.TARGET_PATTERN_EVAL).get())
.isEqualTo(description);
}

Expand All @@ -75,13 +80,14 @@ public void getOrClosestAfterShouldReturnLater() {
.add(BazelProfilePhase.PREPARE, expectedDescription)
.add(BazelProfilePhase.EXECUTE, otherDescription)
.build();
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.EVALUATE).get())
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.TARGET_PATTERN_EVAL).get())
.isEqualTo(expectedDescription);
}

@Test
public void getOrClosestAfterShouldReturnEmpty() {
BazelPhaseDescriptions descriptions = BazelPhaseDescriptions.newBuilder().build();
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.EVALUATE).isEmpty()).isTrue();
assertThat(descriptions.getOrClosestAfter(BazelProfilePhase.TARGET_PATTERN_EVAL).isEmpty())
.isTrue();
}
}
Loading

0 comments on commit f34ee71

Please sign in to comment.