Skip to content

Commit 6a6d1b2

Browse files
joelebacopybara-github
authored andcommitted
Post AnalysisPhaseCompleteEvent for Skymeld.
Many classes, especially the ones collecting metrics around a build, rely on AnalysisPhaseCompleteEvent. For Skymeld, that is problematic since we don't have a clear boundary between analysis/execution. With this cl, we redefine the meaning of AnalysisPhaseCompleteEvent with --skymeld: it's the moment when all analysis-related work is concluded in the build, and blaze is expected to already be deep in execution work when this moment happens. This also means that BEP's analysis_phase_time_in_ms [1] keeps its original meaning with Skymeld: it's the wall time duration between the start and the end of analysis. The difference: it's no longer true that total wall time = analysis wall time + execution wall time, as analysis and execution overlap. To implement this, we introduce AnalysisOperationWatcher, which: - Has a pre-filled expected set of BuildDriverKeys, - Listens to TopLevelEntityAnalysisConcludedEvents which include information of the respective BuildDriverKey, - Removes the BuildDriverKey from the expected set upon receiving the events, and - Posts an AnalysisPhaseCompleteEvent when the expected set is empty. The analysis work of a top level target is considered "concluded" when its analysis either fails or succeeds (which also includes work outside of the main Skyframe evaluation, like action conflict checking and compatibility checking). In case of --nokeep_going and an analysis error occurs, there's no AnalysisPhaseCompleteEvent. This is consistent with the behavior of --noskymeld in the same scenario. [1] https://github.com/bazelbuild/bazel/blob/1941e2b1b5be8596e984b02fd5dd37e3ed25a81f/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto#L948 PiperOrigin-RevId: 461154740 Change-Id: I731c817280e66a6aad3781cc578c60a708a56ceb
1 parent 6b50773 commit 6a6d1b2

15 files changed

+478
-111
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2022 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package com.google.devtools.build.lib.analysis;
15+
16+
import com.google.common.eventbus.EventBus;
17+
import com.google.common.eventbus.Subscribe;
18+
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelEntityAnalysisConcludedEvent;
19+
import com.google.devtools.build.skyframe.SkyKey;
20+
import java.util.Set;
21+
22+
/**
23+
* A watcher for analysis-related work that sends out a signal when all such work in the build is
24+
* done. There's one instance of this class per build.
25+
*/
26+
public class AnalysisOperationWatcher implements AutoCloseable {
27+
// Since the events are fired from within a SkyFunction, it's possible that the same event is
28+
// fired multiple times. A simple counter would therefore not suffice.
29+
private final Set<SkyKey> threadSafeExpectedKeys;
30+
private final EventBus eventBus;
31+
private final AnalysisOperationWatcherFinisher finisher;
32+
33+
private AnalysisOperationWatcher(
34+
Set<SkyKey> threadSafeExpectedKeys,
35+
EventBus eventBus,
36+
AnalysisOperationWatcherFinisher finisher) {
37+
this.threadSafeExpectedKeys = threadSafeExpectedKeys;
38+
this.eventBus = eventBus;
39+
this.finisher = finisher;
40+
}
41+
42+
/** Creates an AnalysisOperationWatcher and registers it with the provided eventBus. */
43+
public static AnalysisOperationWatcher createAndRegisterWithEventBus(
44+
Set<SkyKey> threadSafeExpectedKeys,
45+
EventBus eventBus,
46+
AnalysisOperationWatcherFinisher finisher) {
47+
AnalysisOperationWatcher watcher =
48+
new AnalysisOperationWatcher(threadSafeExpectedKeys, eventBus, finisher);
49+
eventBus.register(watcher);
50+
return watcher;
51+
}
52+
53+
@Subscribe
54+
public void handleTopLevelEntityAnalysisConcluded(TopLevelEntityAnalysisConcludedEvent e) {
55+
if (threadSafeExpectedKeys.isEmpty()) {
56+
return;
57+
}
58+
threadSafeExpectedKeys.remove(e.getAnalyzedTopLevelKey());
59+
if (threadSafeExpectedKeys.isEmpty()) {
60+
finisher.sendAnalysisPhaseCompleteEvent();
61+
}
62+
}
63+
64+
@Override
65+
public void close() {
66+
eventBus.unregister(this);
67+
}
68+
69+
/** A callback to be called when all the expected keys have been analyzed. */
70+
@FunctionalInterface
71+
public interface AnalysisOperationWatcherFinisher {
72+
void sendAnalysisPhaseCompleteEvent();
73+
}
74+
}

src/main/java/com/google/devtools/build/lib/analysis/AnalysisPhaseCompleteEvent.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class AnalysisPhaseCompleteEvent {
3232
private final PackageManagerStatistics pkgManagerStats;
3333
private final TotalAndConfiguredTargetOnlyMetric actionsConstructed;
3434
private final boolean analysisCacheDropped;
35+
private final boolean skymeldEnabled;
3536

3637
public AnalysisPhaseCompleteEvent(
3738
Collection<? extends ConfiguredTarget> topLevelTargets,
@@ -40,12 +41,56 @@ public AnalysisPhaseCompleteEvent(
4041
long timeInMs,
4142
PackageManagerStatistics pkgManagerStats,
4243
boolean analysisCacheDropped) {
44+
this(
45+
topLevelTargets,
46+
targetsConfigured,
47+
actionsConstructed,
48+
timeInMs,
49+
pkgManagerStats,
50+
analysisCacheDropped,
51+
/*skymeldEnabled=*/ false);
52+
}
53+
54+
private AnalysisPhaseCompleteEvent(
55+
Collection<? extends ConfiguredTarget> topLevelTargets,
56+
TotalAndConfiguredTargetOnlyMetric targetsConfigured,
57+
TotalAndConfiguredTargetOnlyMetric actionsConstructed,
58+
long timeInMs,
59+
PackageManagerStatistics pkgManagerStats,
60+
boolean analysisCacheDropped,
61+
boolean skymeldEnabled) {
4362
this.timeInMs = timeInMs;
4463
this.topLevelTargets = ImmutableList.copyOf(topLevelTargets);
4564
this.targetsConfigured = checkNotNull(targetsConfigured);
4665
this.pkgManagerStats = pkgManagerStats;
4766
this.actionsConstructed = checkNotNull(actionsConstructed);
4867
this.analysisCacheDropped = analysisCacheDropped;
68+
this.skymeldEnabled = skymeldEnabled;
69+
}
70+
71+
/**
72+
* A factory method for the AnalysisPhaseCompleteEvent that originates from Skymeld.
73+
*
74+
* <p>This marks the end of the analysis-related work within the build. Contrary to the
75+
* traditional build where there is a distinct separation between the loading/analysis and
76+
* execution phases, overlapping is possible with Skymeld. We are likely already deep into action
77+
* execution when this event is posted.
78+
*/
79+
public static AnalysisPhaseCompleteEvent fromSkymeld(
80+
Collection<? extends ConfiguredTarget> topLevelTargets,
81+
TotalAndConfiguredTargetOnlyMetric targetsConfigured,
82+
TotalAndConfiguredTargetOnlyMetric actionsConstructed,
83+
long timeInMs,
84+
PackageManagerStatistics pkgManagerStats,
85+
boolean analysisCacheDropped) {
86+
return new AnalysisPhaseCompleteEvent(
87+
topLevelTargets,
88+
targetsConfigured,
89+
actionsConstructed,
90+
timeInMs,
91+
pkgManagerStats,
92+
analysisCacheDropped,
93+
/*skymeldEnabled=*/ true);
4994
}
5095

5196
/**
@@ -74,6 +119,14 @@ public boolean wasAnalysisCacheDropped() {
74119
return analysisCacheDropped;
75120
}
76121

122+
/**
123+
* Returns whether this event originated from Skymeld. Some subscribers are incompatible with
124+
* Skymeld and this distinction is required for now.
125+
*/
126+
public boolean isOriginatedFromSkymeld() {
127+
return skymeldEnabled;
128+
}
129+
77130
/**
78131
* Returns package manager statistics.
79132
*/

src/main/java/com/google/devtools/build/lib/analysis/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,16 @@ java_library(
480480
],
481481
)
482482

483+
java_library(
484+
name = "analysis_operation_watcher",
485+
srcs = ["AnalysisOperationWatcher.java"],
486+
deps = [
487+
"//src/main/java/com/google/devtools/build/lib/skyframe:top_level_status_events",
488+
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
489+
"//third_party:guava",
490+
],
491+
)
492+
483493
java_library(
484494
name = "analysis_phase_complete_event",
485495
srcs = ["AnalysisPhaseCompleteEvent.java"],
@@ -625,6 +635,7 @@ java_library(
625635
"//src/main/java/com/google/devtools/build/lib/profiler",
626636
"//src/main/java/com/google/devtools/build/lib/skyframe:aspect_key_creator",
627637
"//src/main/java/com/google/devtools/build/lib/skyframe:build_configuration",
638+
"//src/main/java/com/google/devtools/build/lib/skyframe:build_result_listener",
628639
"//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key",
629640
"//src/main/java/com/google/devtools/build/lib/skyframe:coverage_report_value",
630641
"//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster",

src/main/java/com/google/devtools/build/lib/analysis/BuildView.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey;
8181
import com.google.devtools.build.lib.skyframe.AspectKeyCreator.TopLevelAspectsKey;
8282
import com.google.devtools.build.lib.skyframe.BuildConfigurationKey;
83+
import com.google.devtools.build.lib.skyframe.BuildResultListener;
8384
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
8485
import com.google.devtools.build.lib.skyframe.CoverageReportValue;
8586
import com.google.devtools.build.lib.skyframe.PrepareAnalysisPhaseValue;
@@ -213,7 +214,8 @@ public AnalysisResult update(
213214
BugReporter bugReporter,
214215
boolean includeExecutionPhase,
215216
int mergedPhasesExecutionJobsCount,
216-
@Nullable ResourceManager resourceManager)
217+
@Nullable ResourceManager resourceManager,
218+
@Nullable BuildResultListener buildResultListener)
217219
throws ViewCreationFailedException, InvalidConfigurationException, InterruptedException,
218220
BuildFailedException, TestExecException {
219221
logger.atInfo().log("Starting analysis");
@@ -410,9 +412,6 @@ public AnalysisResult update(
410412
} else {
411413
skyframeExecutor.setRuleContextConstraintSemantics(
412414
(RuleContextConstraintSemantics) ruleClassProvider.getConstraintSemantics());
413-
// For the Skymeld code path, we expect action execution and hence a non-null resource
414-
// manager.
415-
Preconditions.checkNotNull(resourceManager);
416415
skyframeAnalysisResult =
417416
skyframeBuildView.analyzeAndExecuteTargets(
418417
eventHandler,
@@ -425,7 +424,8 @@ public AnalysisResult update(
425424
explicitTargetPatterns,
426425
eventBus,
427426
bugReporter,
428-
resourceManager,
427+
Preconditions.checkNotNull(resourceManager), // non-null for skymeld.
428+
Preconditions.checkNotNull(buildResultListener), // non-null for skymeld.
429429
keepGoing,
430430
viewOptions.strictConflictChecks,
431431
checkForActionConflicts,

src/main/java/com/google/devtools/build/lib/buildtool/AnalysisAndExecutionPhaseRunner.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ private static AnalysisAndExecutionResult runAnalysisAndExecutionPhase(
207207
env.getRuntime().getBugReporter(),
208208
/*includeExecutionPhase=*/ true,
209209
request.getBuildOptions().jobs,
210-
env.getLocalResourceManager());
210+
env.getLocalResourceManager(),
211+
env.getBuildResultListener());
211212
}
212213

213214
/**

src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ private static AnalysisResult runAnalysisPhase(
242242
env.getRuntime().getBugReporter(),
243243
/*includeExecutionPhase=*/ false,
244244
/*mergedPhasesExecutionJobsCount=*/ 0,
245-
/*resourceManager=*/ null);
245+
/*resourceManager=*/ null,
246+
/*buildResultListener=*/ null);
246247
} catch (BuildFailedException | TestExecException unexpected) {
247248
throw new IllegalStateException("Unexpected execution exception type: ", unexpected);
248249
}

src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,10 @@ public void loadingComplete(LoadingPhaseCompleteEvent event) {
555555

556556
@Subscribe
557557
public synchronized void analysisComplete(AnalysisPhaseCompleteEvent event) {
558+
// TODO(b/215335350): Make this work with Skymeld. Ignore for now.
559+
if (event.isOriginatedFromSkymeld()) {
560+
return;
561+
}
558562
String analysisSummary = stateTracker.analysisComplete();
559563
handle(Event.info(null, analysisSummary));
560564
}

src/main/java/com/google/devtools/build/lib/skyframe/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ java_library(
106106
":build_driver_key",
107107
":build_driver_value",
108108
":build_info_collection_value",
109+
":build_result_listener",
109110
":bzl_compile",
110111
":bzl_load_value",
111112
":cached_bzl_load_value_and_deps",
@@ -228,7 +229,9 @@ java_library(
228229
"//src/main/java/com/google/devtools/build/lib/actions:resource_manager",
229230
"//src/main/java/com/google/devtools/build/lib/actions:thread_state_receiver",
230231
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
232+
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_operation_watcher",
231233
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_options",
234+
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_phase_complete_event",
232235
"//src/main/java/com/google/devtools/build/lib/analysis:aspect_collection",
233236
"//src/main/java/com/google/devtools/build/lib/analysis:blaze_directories",
234237
"//src/main/java/com/google/devtools/build/lib/analysis:buildinfo/build_info_collection",
@@ -2256,6 +2259,7 @@ java_library(
22562259
"//src/main/java/com/google/devtools/build/lib/events",
22572260
"//src/main/java/com/google/devtools/build/lib/packages",
22582261
"//src/main/java/com/google/devtools/build/lib/pkgcache",
2262+
"//src/main/java/com/google/devtools/build/lib/skyframe:top_level_status_events",
22592263
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
22602264
"//src/main/java/com/google/devtools/build/skyframe",
22612265
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
@@ -2650,6 +2654,7 @@ java_library(
26502654
"//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration",
26512655
"//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
26522656
"//src/main/java/com/google/devtools/build/lib/events",
2657+
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
26532658
"//third_party:auto_value",
26542659
],
26552660
)

src/main/java/com/google/devtools/build/lib/skyframe/BuildDriverFunction.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.AspectAnalyzedEvent;
5757
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.SomeExecutionStartedEvent;
5858
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TestAnalyzedEvent;
59+
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelEntityAnalysisConcludedEvent;
5960
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelTargetAnalyzedEvent;
6061
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelTargetPendingExecutionEvent;
6162
import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelTargetSkippedEvent;
@@ -146,6 +147,10 @@ public SkyValue compute(SkyKey skyKey, Environment env)
146147
if (topLevelSkyValue instanceof ConfiguredTargetValue) {
147148
ConfiguredTarget configuredTarget =
148149
((ConfiguredTargetValue) topLevelSkyValue).getConfiguredTarget();
150+
// At this point, the target is considered "analyzed". It's important that this event is sent
151+
// before the TopLevelEntityAnalysisConcludedEvent: when the last of the analysis work is
152+
// concluded, we need to have the *complete* list of analyzed targets ready in
153+
// BuildResultListener.
149154
env.getListener().post(TopLevelTargetAnalyzedEvent.create(configuredTarget));
150155

151156
BuildConfigurationValue buildConfigurationValue =
@@ -178,6 +183,9 @@ public SkyValue compute(SkyKey skyKey, Environment env)
178183
TestAnalyzedEvent.create(
179184
configuredTarget, buildConfigurationValue, /*isSkipped=*/ true));
180185
}
186+
// Only send the event now to include the compatibility check in the measurement for
187+
// time spent on analysis work.
188+
env.getListener().post(TopLevelEntityAnalysisConcludedEvent.create(buildDriverKey));
181189
// We consider the evaluation of this BuildDriverKey successful at this point, even when
182190
// the target is skipped.
183191
return new BuildDriverValue(topLevelSkyValue, /*skipped=*/ true);
@@ -187,6 +195,7 @@ public SkyValue compute(SkyKey skyKey, Environment env)
187195
}
188196
}
189197

198+
env.getListener().post(TopLevelEntityAnalysisConcludedEvent.create(buildDriverKey));
190199
env.getListener()
191200
.post(
192201
TopLevelTargetPendingExecutionEvent.create(
@@ -199,6 +208,7 @@ public SkyValue compute(SkyKey skyKey, Environment env)
199208
env,
200209
topLevelArtifactContext);
201210
} else {
211+
env.getListener().post(TopLevelEntityAnalysisConcludedEvent.create(buildDriverKey));
202212
requestAspectExecution((TopLevelAspectsValue) topLevelSkyValue, env, topLevelArtifactContext);
203213
}
204214

0 commit comments

Comments
 (0)