From 4dc9946dca2fdcccd45e9038f87c518aed946d23 Mon Sep 17 00:00:00 2001 From: Sara Adams Date: Tue, 28 Nov 2023 15:38:16 +0100 Subject: [PATCH] [DataProvider] Add provider for metrics on caching status and execution location This data provider scans all actions and splits them into: * remote cache hit * remote cache miss * remote cache not checked as well as (for non-cache-hits): * executed locally * executed remotely * execution location not reported "Internal" Bazel actions are included in "remote cache not checked" and "execution location not reported". While I invested ample time in trying to single internal actions out reliably, I did not succeed. I'm not sure it's possible with the information currently written to profiles. A TODO to look into separating out "internal" actions is in the code. Contributes to #90 Signed-off-by: Sara Adams --- .../CachingAndExecutionMetrics.java | 221 ++++++++++++++++++ ...achingAndExecutionMetricsDataProvider.java | 92 ++++++++ .../dataproviders/DataProviderUtil.java | 1 + .../dataproviders/RemoteCacheMetrics.java | 20 ++ ...ngAndExecutionMetricsDataProviderTest.java | 157 +++++++++++++ .../dataproviders/DataProvidersTestSuite.java | 1 + 6 files changed, 492 insertions(+) create mode 100644 analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetrics.java create mode 100644 analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProvider.java create mode 100644 analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProviderTest.java diff --git a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetrics.java b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetrics.java new file mode 100644 index 0000000..93a5f6f --- /dev/null +++ b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetrics.java @@ -0,0 +1,221 @@ +/* + * 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.core.Datum; +import javax.annotation.Nullable; + +/** + * Statistics on the number of actions included and their caching and execution status. + * + *

While it is not enforced, the expectancy is that: + *

  • All actions have some RC status:
    + * {@link #getActions} = {@link #getActionsWithRcMiss()} + {@link #getActionsWithRcHit()} + + * {@link #getActionsWithoutRc()} + *
  • All actions that were not cache hits are executed:
    + * {@link #getActionsWithRcMiss()} + {@link #getActionsWithoutRc()} = {@link + * #getActionsExecutedLocally()} + {@link #getActionsExecutedRemotely()} + */ +public class CachingAndExecutionMetrics implements Datum { + private final long actions; + private final long actionsWithRcHit; + private final long actionsWithRcMissLocallyExecuted; + private final long actionsWithRcMissRemotelyExecuted; + private final long actionsWithRcMissExecNotReported; + private final long actionsWithoutRcLocallyExecuted; + private final long actionsWithoutRcRemotelyExecuted; + private final long actionsWithoutRcExecNotReported; + + public CachingAndExecutionMetrics( + long actions, + long actionsWithRcHit, + long actionsWithRcMissLocallyExecuted, + long actionsWithRcMissRemotelyExecuted, + long actionsWithRcMissExecNotReported, + long actionsWithoutRcLocallyExecuted, + long actionsWithoutRcRemotelyExecuted, + long actionsWithoutRcExecNotReported) { + this.actions = actions; + this.actionsWithRcHit = actionsWithRcHit; + this.actionsWithRcMissLocallyExecuted = actionsWithRcMissLocallyExecuted; + this.actionsWithRcMissRemotelyExecuted = actionsWithRcMissRemotelyExecuted; + this.actionsWithRcMissExecNotReported = actionsWithRcMissExecNotReported; + this.actionsWithoutRcLocallyExecuted = actionsWithoutRcLocallyExecuted; + this.actionsWithoutRcRemotelyExecuted = actionsWithoutRcRemotelyExecuted; + this.actionsWithoutRcExecNotReported = actionsWithoutRcExecNotReported; + } + + /** The number of actions processed. */ + public long getActions() { + return actions; + } + + /** The number of actions processed that checked a remote cache and there was a match. */ + public long getActionsWithRcHit() { + return actionsWithRcHit; + } + + /** The number of actions processed that checked a remote cache and there was no match. */ + public long getActionsWithRcMiss() { + return actionsWithRcMissLocallyExecuted + + actionsWithRcMissRemotelyExecuted + + actionsWithRcMissExecNotReported; + } + + /** + * The number of actions processed that had a remote cache miss and that were executed locally. + */ + public long getActionsWithRcMissLocallyExecuted() { + return actionsWithRcMissLocallyExecuted; + } + + /** + * The number of actions processed that had a remote cache miss and that were executed remotely. + */ + public long getActionsWithRcMissRemotelyExecuted() { + return actionsWithRcMissRemotelyExecuted; + } + + /** + * The number of actions processed that had a remote cache miss and where no event indicating + * execution location was found. + */ + public long getActionsWithRcMissExecNotReported() { + return actionsWithRcMissExecNotReported; + } + + /** The number of actions processed that did not check a remote cache. */ + public long getActionsWithoutRc() { + return actionsWithoutRcLocallyExecuted + + actionsWithoutRcRemotelyExecuted + + actionsWithoutRcExecNotReported; + } + + /** + * The number of actions processed that did not check a remote cache and that were executed + * locally. + */ + public long getActionsWithoutRcLocallyExecuted() { + return actionsWithoutRcLocallyExecuted; + } + + /** + * The number of actions processed that did not check a remote cache and that were executed + * remotely. + */ + public long getActionsWithoutRcRemotelyExecuted() { + return actionsWithoutRcRemotelyExecuted; + } + + /** + * The number of actions processed that did not check a remote cache and where no event indicating + * execution location was found. This includes internal Bazel events, such as creating symlinks. + */ + public long getActionsWithoutRcExecNotReported() { + return actionsWithoutRcExecNotReported; + } + + /** The number of actions processed that were executed locally. */ + public long getActionsExecutedLocally() { + return actionsWithRcMissLocallyExecuted + actionsWithoutRcLocallyExecuted; + } + + /** The number of actions processed that were executed remotely. */ + public long getActionsExecutedRemotely() { + return actionsWithRcMissRemotelyExecuted + actionsWithoutRcRemotelyExecuted; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public String getEmptyReason() { + return null; + } + + @Override + public String getDescription() { + return "Metrics on the processed actions' caching and execution status."; + } + + private static String summarize(int padTo, long part, long total, @Nullable String suffix) { + return String.format( + "%" + padTo + "d %,6.2f%%%s", + part, + 100f * part / total, + suffix == null ? "" : " " + suffix); + } + + @Override + public String getSummary() { + int width = String.valueOf(actions).length(); + StringBuilder sb = new StringBuilder(); + sb.append("Actions: ____________________________ "); + sb.append(actions); + sb.append("\n Remote cache hits: ______________ "); + sb.append(summarize(width, actionsWithRcHit, actions, "of all actions")); + sb.append("\n Remote cache misses: ____________ "); + sb.append(summarize(width, getActionsWithRcMiss(), actions, "of all actions")); + sb.append("\n Executed locally: "); + sb.append( + summarize( + width, + actionsWithRcMissLocallyExecuted, + getActionsWithRcMiss(), + "of all cache misses")); + sb.append("\n Executed remotely: "); + sb.append( + summarize( + width, + actionsWithRcMissRemotelyExecuted, + getActionsWithRcMiss(), + "of all cache misses")); + sb.append("\n Not reported: "); + sb.append( + summarize( + width, + actionsWithRcMissExecNotReported, + getActionsWithRcMiss(), + "of all cache misses")); + sb.append("\n Remote cache not checked: _______ "); + sb.append(summarize(width, getActionsWithoutRc(), actions, "of all actions")); + sb.append("\n Executed locally: "); + sb.append( + summarize( + width, actionsWithoutRcLocallyExecuted, getActionsWithoutRc(), "of all cache skips")); + sb.append("\n Executed remotely: "); + sb.append( + summarize( + width, actionsWithoutRcRemotelyExecuted, getActionsWithoutRc(), "of all cache skips")); + sb.append("\n Not reported (e.g. internal): "); + sb.append( + summarize( + width, actionsWithoutRcExecNotReported, getActionsWithoutRc(), "of all cache skips")); + sb.append("\n\n Executed locally: _______________ "); + sb.append(summarize(width, getActionsExecutedLocally(), actions, "of all actions")); + sb.append("\n Executed remotely: ______________ "); + sb.append(summarize(width, getActionsExecutedRemotely(), actions, "of all actions")); + sb.append("\n Execution not reported: _________ "); + sb.append( + summarize( + width, + actions - getActionsExecutedLocally() - getActionsExecutedRemotely(), + actions, + "of all actions")); + return sb.toString(); + } +} diff --git a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProvider.java b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProvider.java new file mode 100644 index 0000000..71c8ac3 --- /dev/null +++ b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProvider.java @@ -0,0 +1,92 @@ +/* + * 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.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; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A {@link DataProvider} that supplies data on caching and execution location. Note that + * `disk_cache` is also a remote cache, even though it interacts with the local disk. + */ +public class CachingAndExecutionMetricsDataProvider extends DataProvider { + + @Override + public List> getSuppliers() { + return List.of( + DatumSupplierSpecification.of( + CachingAndExecutionMetrics.class, DatumSupplier.memoized(this::getMetrics))); + } + + @VisibleForTesting + CachingAndExecutionMetrics getMetrics() + throws InvalidProfileException, MissingInputException, NullDatumException { + var localActions = getDataManager().getDatum(LocalActions.class); + + long actions = localActions.size(); + AtomicLong cacheHit = new AtomicLong(); + AtomicLong cacheCheck = new AtomicLong(); + AtomicLong cacheMissLocalExec = new AtomicLong(); + AtomicLong cacheMissRemoteExec = new AtomicLong(); + AtomicLong cacheMissExecNotReported = new AtomicLong(); + AtomicLong nocacheLocalExec = new AtomicLong(); + AtomicLong nocacheRemoteExec = new AtomicLong(); + AtomicLong nocacheExecNotReported = new AtomicLong(); + localActions.parallelStream() + .forEach( + action -> { + var localExec = action.isExecutedLocally(); + var remoteExec = action.isExecutedRemotely(); + if (action.isRemoteCacheHit()) { + cacheCheck.getAndIncrement(); + cacheHit.getAndIncrement(); + } else if (action.hasRemoteCacheCheck()) { + cacheCheck.getAndIncrement(); + if (localExec) { + cacheMissLocalExec.getAndIncrement(); + } else if (remoteExec) { + cacheMissRemoteExec.getAndIncrement(); + } else { + cacheMissExecNotReported.getAndIncrement(); + } + } else { + if (localExec) { + nocacheLocalExec.getAndIncrement(); + } else if (remoteExec) { + nocacheRemoteExec.getAndIncrement(); + } else { + // TODO: Can we separate out Bazel "internal" actions? + nocacheExecNotReported.getAndIncrement(); + } + } + }); + return new CachingAndExecutionMetrics( + actions, + cacheHit.get(), + cacheMissLocalExec.get(), + cacheMissRemoteExec.get(), + cacheMissExecNotReported.get(), + nocacheLocalExec.get(), + nocacheRemoteExec.get(), + nocacheExecNotReported.get()); + } +} diff --git a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/DataProviderUtil.java b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/DataProviderUtil.java index fa503f4..3741bcf 100644 --- a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/DataProviderUtil.java +++ b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/DataProviderUtil.java @@ -34,6 +34,7 @@ public static List getAllDataProviders() { new ActionStatsDataProvider(), new BazelPhasesDataProvider(), new BazelVersionDataProvider(), + new CachingAndExecutionMetricsDataProvider(), new CriticalPathDurationDataProvider(), new EstimatedCoresDataProvider(), new FlagValueDataProvider(), diff --git a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/RemoteCacheMetrics.java b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/RemoteCacheMetrics.java index e15a102..23a8df4 100644 --- a/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/RemoteCacheMetrics.java +++ b/analyzer/java/com/engflow/bazel/invocation/analyzer/dataproviders/RemoteCacheMetrics.java @@ -56,6 +56,26 @@ public class RemoteCacheMetrics implements Datum { this.uploadOutputsDuration = Preconditions.checkNotNull(uploadOutputsDuration); } + public int getCacheChecks() { + return cacheChecks; + } + + public int getCacheMisses() { + return cacheMisses; + } + + public Duration getCacheCheckDuration() { + return cacheCheckDuration; + } + + public Duration getDownloadOutputsDuration() { + return downloadOutputsDuration; + } + + public Duration getUploadOutputsDuration() { + return uploadOutputsDuration; + } + @Override public boolean isEmpty() { return cacheChecks == 0; diff --git a/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProviderTest.java b/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProviderTest.java new file mode 100644 index 0000000..960b7da --- /dev/null +++ b/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/CachingAndExecutionMetricsDataProviderTest.java @@ -0,0 +1,157 @@ +/* + * 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 static com.engflow.bazel.invocation.analyzer.bazelprofile.BazelProfileConstants.CAT_LOCAL_ACTION_EXECUTION; +import static com.engflow.bazel.invocation.analyzer.bazelprofile.BazelProfileConstants.CAT_REMOTE_ACTION_CACHE_CHECK; +import static com.engflow.bazel.invocation.analyzer.bazelprofile.BazelProfileConstants.CAT_REMOTE_ACTION_EXECUTION; +import static com.engflow.bazel.invocation.analyzer.bazelprofile.BazelProfileConstants.CAT_REMOTE_OUTPUT_DOWNLOAD; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.engflow.bazel.invocation.analyzer.EventThreadBuilder; +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.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +public class CachingAndExecutionMetricsDataProviderTest extends DataProviderUnitTestBase { + private CachingAndExecutionMetricsDataProvider provider; + private LocalActions localActions; + + @Before + public void setupTest() throws Exception { + provider = new CachingAndExecutionMetricsDataProvider(); + provider.register(dataManager); + when(dataManager.getDatum(LocalActions.class)).thenAnswer(i -> localActions); + super.dataProvider = provider; + } + + @Test + public void shouldReturnEmptyOnEmptyLocalActions() + throws InvalidProfileException, MissingInputException, NullDatumException { + localActions = LocalActions.create(ImmutableList.of()); + + var metrics = provider.getMetrics(); + assertThat(metrics.getActions()).isEqualTo(0); + assertThat(metrics.getActionsWithRcHit()).isEqualTo(0); + assertThat(metrics.getActionsWithRcMiss()).isEqualTo(0); + assertThat(metrics.getActionsWithRcMissLocallyExecuted()).isEqualTo(0); + assertThat(metrics.getActionsWithRcMissRemotelyExecuted()).isEqualTo(0); + assertThat(metrics.getActionsWithRcMissExecNotReported()).isEqualTo(0); + assertThat(metrics.getActionsWithoutRc()).isEqualTo(0); + assertThat(metrics.getActionsWithoutRcLocallyExecuted()).isEqualTo(0); + assertThat(metrics.getActionsWithoutRcRemotelyExecuted()).isEqualTo(0); + assertThat(metrics.getActionsWithoutRcExecNotReported()).isEqualTo(0); + assertThat(metrics.getActionsExecutedLocally()).isEqualTo(0); + assertThat(metrics.getActionsExecutedRemotely()).isEqualTo(0); + } + + @Test + public void shouldReturnMetrics() + throws InvalidProfileException, MissingInputException, NullDatumException { + var thread = new EventThreadBuilder(1, 1); + List all = new ArrayList(); + int cacheHits = 1; + int cacheMissesLocalExec = 2; + int cacheMissesRemoteExec = 4; + int cacheMissesUnreportedExec = 8; + int nocacheLocalExec = 16; + int nocacheRemoteExec = 32; + int nocacheUnreportedExec = 64; + for (int i = 0; i < cacheHits; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("cache hit " + i, "Work", 0, 5), + List.of( + thread.related(0, 2, CAT_REMOTE_ACTION_CACHE_CHECK), + thread.related(2, 3, CAT_REMOTE_OUTPUT_DOWNLOAD)))); + } + for (int i = 0; i < cacheMissesLocalExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("cache miss local exec " + i, "Work", 0, 5), + List.of( + thread.related(0, 2, CAT_REMOTE_ACTION_CACHE_CHECK), + thread.related(2, 3, CAT_LOCAL_ACTION_EXECUTION)))); + } + for (int i = 0; i < cacheMissesRemoteExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("cache miss remote exec " + i, "Work", 0, 5), + List.of( + thread.related(0, 2, CAT_REMOTE_ACTION_CACHE_CHECK), + thread.related(2, 1, CAT_REMOTE_ACTION_EXECUTION), + thread.related(3, 2, CAT_REMOTE_OUTPUT_DOWNLOAD)))); + } + for (int i = 0; i < cacheMissesUnreportedExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("cache miss unreported exec " + i, "Work", 0, 5), + List.of(thread.related(0, 2, CAT_REMOTE_ACTION_CACHE_CHECK)))); + } + for (int i = 0; i < nocacheLocalExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("mo cache check exec " + i, "Work", 0, 5), + List.of(thread.related(2, 3, CAT_LOCAL_ACTION_EXECUTION)))); + } + for (int i = 0; i < nocacheRemoteExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("no cache check remote exec " + i, "Work", 0, 5), + List.of( + thread.related(2, 1, CAT_REMOTE_ACTION_EXECUTION), + thread.related(3, 2, CAT_REMOTE_OUTPUT_DOWNLOAD)))); + } + for (int i = 0; i < nocacheUnreportedExec; i++) { + all.add( + new LocalActions.LocalAction( + thread.actionProcessingAction("no cache check unreported exec " + i, "Work", 0, 5), + List.of())); + } + localActions = LocalActions.create(all); + + var metrics = provider.getMetrics(); + assertThat(metrics.getActions()) + .isEqualTo( + cacheHits + + cacheMissesLocalExec + + cacheMissesRemoteExec + + cacheMissesUnreportedExec + + nocacheLocalExec + + nocacheRemoteExec + + nocacheUnreportedExec); + assertThat(metrics.getActionsWithRcHit()).isEqualTo(cacheHits); + assertThat(metrics.getActionsWithRcMiss()) + .isEqualTo(cacheMissesLocalExec + cacheMissesRemoteExec + cacheMissesUnreportedExec); + assertThat(metrics.getActionsWithRcMissLocallyExecuted()).isEqualTo(cacheMissesLocalExec); + assertThat(metrics.getActionsWithRcMissRemotelyExecuted()).isEqualTo(cacheMissesRemoteExec); + assertThat(metrics.getActionsWithRcMissExecNotReported()).isEqualTo(cacheMissesUnreportedExec); + assertThat(metrics.getActionsWithoutRc()) + .isEqualTo(nocacheLocalExec + nocacheRemoteExec + nocacheUnreportedExec); + assertThat(metrics.getActionsWithoutRcLocallyExecuted()).isEqualTo(nocacheLocalExec); + assertThat(metrics.getActionsWithoutRcRemotelyExecuted()).isEqualTo(nocacheRemoteExec); + assertThat(metrics.getActionsWithoutRcExecNotReported()).isEqualTo(nocacheUnreportedExec); + assertThat(metrics.getActionsExecutedLocally()) + .isEqualTo(cacheMissesLocalExec + nocacheLocalExec); + assertThat(metrics.getActionsExecutedRemotely()) + .isEqualTo(cacheMissesRemoteExec + nocacheRemoteExec); + } +} diff --git a/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/DataProvidersTestSuite.java b/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/DataProvidersTestSuite.java index 4b3aff1..681eefa 100644 --- a/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/DataProvidersTestSuite.java +++ b/analyzer/javatests/com/engflow/bazel/invocation/analyzer/dataproviders/DataProvidersTestSuite.java @@ -25,6 +25,7 @@ BazelVersionTest.class, BazelVersionDataProviderTest.class, BazelProfilePhaseTest.class, + CachingAndExecutionMetricsDataProviderTest.class, CriticalPathDurationDataProviderTest.class, EstimatedCoresDataProviderTest.class, FlagValueDataProviderTest.class,